最近、List.permute を使用してリストをシャッフルするFisher-Yates シャッフルを実装しましたが、リストのサイズが大きくなるにつれて、パフォーマンスが大幅に低下することに気付きました。これは、アルゴリズムが配列で動作していると想定している一方で、順列は O(n) であるインデックスによってリスト要素にアクセスする必要があるという事実によるものと思われます。
これを確認するために、リストに順列を適用してその要素を逆にし、リストで直接作業を比較し、リストを配列に変換してリストに戻すことを試みました。
let permute i max = max - i - 1
let test = [ 0 .. 10000 ]
let rev1 list =
let perm i = permute i (List.length list)
List.permute perm list
let rev2 list =
let array = List.toArray list
let perm i = permute i (Array.length array)
Array.permute perm array |> Array.toList
次の結果が得られます。これは、私の仮定を裏付ける傾向があります。
rev1 test;;
Real: 00:00:00.283, CPU: 00:00:00.265, GC gen0: 0, gen1: 0, gen2: 0
rev2 test;;
Real: 00:00:00.003, CPU: 00:00:00.000, GC gen0: 0, gen1: 0, gen2: 0
私の質問は次のとおりです。
1) パフォーマンス上の理由から List.permute を避けるべきですか? それに関連して、List.permute の実装は自動的に Array への変換を舞台裏で行うべきではありませんか?
2) 配列を使用する以外に、要素のシャッフルなど、このタイプの作業に適したより機能的な方法/データ構造はありますか? それとも、これは単に配列が使用する適切なデータ構造であるという問題ですか?