2

Haskell を使用して Verlet インテグレーターを作成し、重力をモデル化しています。インテグレータは、オブジェクトの最初の 2 つの位置をシードとして使用し、その後残りを生成します。

Haskell でこれを行う良い方法は、無限リストを使用することだと思いました。ただし、実装すると、長時間非常にゆっくりと実行されることがわかりました (Haskell 1700 のタイム ステップ: 12 秒、Python 1700 のタイム ステップ: < 1 秒)。

同様のパフォーマンスを持つ 1 次元積分器の関連コードを次に示します。

verletStep dt acc xn xn1 = 2*xn1 - xn + (acc xn1)*dt*dt

verlet dt acc x0 x1 = x0 : x1 : next (verlet dt acc x0 x1)
  where
    next (xn : xs@(xn1:_)) = (verletStep dt acc xn xn1) : next xs

無限リストの生成にも使用zipWithしてみましたが、同様のパフォーマンスが得られます。

なぜこれに時間がかかるのですか?ガベージ コレクション自体は約 5 秒です。これをより速く実行する良い方法はありますか?

4

1 に答える 1

5

この定義...

verlet dt acc x0 x1 = x0 : x1 : next (verlet dt acc x0 x1)
  where
    next (xn : xs@(xn1:_)) = (verletStep dt acc xn xn1) : next xs

... verlet dt acc x0 x1不必要に何度も計算され、不要なリストがたくさん作成されます。これは、時間ステップを手で計算することで確認できます。

verlet dt acc x0 x1
x0 : x1 : next (verlet dt acc x0 x1)
x0 : x1 : next (x0 : x1 : next (verlet dt acc x0 x1))
x0 : x1 : (verletStep dt acc x0 x1) : next (x1 : next (verlet dt acc x0 x1))

解決策は、不要なリスト作成をなくすことです:

verlet dt acc x0 x1 = x0 : x1 : x2 : drop 2 (verlet dt acc x1 x2)
  where
     x2 = verletStep dt acc x0 x1

drop 2リストの最初の 2 つの要素を削除します (この場合は、x1既にx2先頭に追加した と )。は、2 番目の位置と、新しく計算された 3verlet番目の位置 で再帰的に呼び出されます。( が同じ引数で再帰的に呼び出される元の定義と比較してください。疑念が生じるはずです。)x1x2verlet

于 2015-08-07T10:57:26.227 に答える