Mathematica の繰り返しがおもしろかったので,ちょっと関連する話をしてみようと思います.
結論から言うと,「繰り返しのコードを書いたら負け」ということです.高校生が行列やベクトルを勉強する時に,「成分書いたら負け」と教わるのと同じ感じです.簡単な例を使って調べてみましょう.
100万個の乱数からなるリストxListを作り,各要素のSinを求めます.いろんな方法が考えられますが,Sinの計算にかかる時間はピンキリです.AbsoluteTimingで計ってみましょう.
Core i7 930(HTはOff), 主記憶6GB,Windows 7 64-bit上のMathematica 8.0.0で試します.
空のリスト+繰り返し
まずはやってはいけない例から.空のリストに結果を追加するような方法は,時間がかかりすぎます.バージョン6で導入された添字を使わない方法(いわゆるforeach形式)と,従来からある添字を使う方法,どちらも測定をあきらめました.
AbsoluteTiming[ result = {}; Do[AppendTo[result, Sin[x]], {x, xList}];] $Aborted
AbsoluteTiming[ result = {}; Do[AppendTo[result, Sin[xList[[i]]]], {i, 1, Length[xList]}];] $Aborted
固定サイズのリスト+繰り返し
格納するためのリストのサイズを最初に決めておくようにすれば,現実的な時間で計算できます.添字を使わない方法と添字を使う方法との間に,性能の違いはほとんどありません.
AbsoluteTiming[ result = Table[0, {Length[xList]}]; Module[{i = 1}, Do[result[[i++]] = Sin[x], {x, xList}]];] {3.3941941, Null}
AbsoluteTiming[ result = Table[0, {Length[xList]}]; Do[result[[i]] = Sin[xList[[i]]], {i, 1, Length[xList]}];] {3.3291904, Null}
リストの生成,逐次計算
リストを生成するための関数であるMapやTableを使えばかなり早くなります.Mapを使う方法と添字を使うTableの性能は同じですが,添字を使わないTableは,かなり性能がよさそうです.
AbsoluteTiming[result = Map[Sin, xList];] {0.1320076, Null}
AbsoluteTiming[result = Table[Sin[xList[[i]]], {i, 1, Length[xList]}];] {0.1320076, Null}
AbsoluteTiming[result = Table[Sin[x], {x, xList}];] {0.0940054, Null}
リストの生成,並列計算
バージョン7で導入された組み込み並列計算のためのParallelTableやParallelMapは,Sinのような簡単な計算では威力を発揮できません.
AbsoluteTiming[result = ParallelTable[Sin[x], {x, xList}];] {1.6220928, Null}
AbsoluteTiming[result = ParallelMap[Sin, xList];] {1.0480599, Null}
AbsoluteTiming[result = ParallelTable[Sin[xList[[i]]], {i, 1, Length[xList]}];] {0.5030287, Null}
添字を使う方法と添字を使わない方法の性能差が,逐次計算の場合と逆になっています.
Listable+ベクトル化
実を言うとSinは,引数がリストなら自動的に各要素に適用されるListableな関数なので,MapもTableも不要です.さらに,バージョン5.2で導入された自動ベクトル化をサポートしているので,次のように単純に書くと圧倒的な速さになります.ベクトル化をちゃんとサポートしたコンパイラを使わないと,CやC++でもこの速度は出ないのではないでしょうか.
AbsoluteTiming[answer = Sin[xList];] {0.0080005, Null}
というわけで,最初に書いたとおりの結論,「繰り返しのコードを書いたら負け」になります.