私はHaskellで動的プログラミングをいじっています。このテーマについて私が見た事実上すべてのチュートリアルは、メモ化と Array 型の怠惰に基づいた、同じ非常に洗練されたアルゴリズムを提供します。これらの例に触発されて、テストとして次のアルゴリズムを作成しました。
-- pascal n returns the nth entry on the main diagonal of pascal's triangle
-- (mod a million for efficiency)
pascal :: Int -> Int
pascal n = p ! (n,n) where
p = listArray ((0,0),(n,n)) [f (i,j) | i <- [0 .. n], j <- [0 .. n]]
f :: (Int,Int) -> Int
f (_,0) = 1
f (0,_) = 1
f (i,j) = (p ! (i, j-1) + p ! (i-1, j)) `mod` 1000000
私の唯一の問題は効率です。GHC の -O2 を使用しても、このプログラムは を計算するのに 1.6 秒かかりpascal 1000
、同等の最適化されていない C++ プログラムよりも約 160 倍遅くなります。そして、ギャップは、より大きな入力で拡大するだけです.
data-memocombinators ライブラリのような提案された代替手段とともに、上記のコードの可能なすべての順列を試したようですが、それらはすべて同じかそれより悪いパフォーマンスでした。私が試していないことの 1 つは、ST モナドです。これは、プログラムを C バージョンよりもわずかに遅く実行するように作成できると確信しています。しかし、慣用的なHaskellで書きたいのですが、なぜ慣用的なバージョンがそれほど効率的でないのか理解できません。2 つの質問があります。
上記のコードが非効率なのはなぜですか? 各エントリで算術演算を使用して、マトリックスを単純に反復するように見えます。明らかに、Haskell は私が理解できない裏で何かを行っています。
ステートレスで再帰的な定式化を犠牲にすることなく (ST Monad で可変配列を使用する実装と比較して) 効率を大幅に向上させる方法はありますか (C プログラムの実行時間の最大 10 倍から 15 倍)。
どうもありがとう。
編集:使用される配列モジュールは標準の Data.Array です