Array#shuffle を使用する場合、Ruby はカスタムのランダマイザーの使用を許可し、Random
それを利用するためのクラスも提供します。次の例では、シード値が 48 のクラスを使用しています。
array = [1,2,3,4,5,6,7,8,9,10]
array.shuffle(random: Random.new(48)) # => [8,6,3,7,10,9,5,2,4,1]
シャッフルされた配列で値が最初に何回出現したかを確認するための小さなモノビット テストを作成しました。
deck = (1..10).to_a
counts = Hash.new(0)
rng = Random.new
50000.times do
counts[deck.shuffle(random: rng).first] += 1
end
1.upto(10) do |card|
puts "#{card}:\t#{counts[card]}"
end
出力は次のようになります。
1: 4942
2: 5100
3: 4938
4: 4960
5: 5024
6: 4992
7: 5184
8: 4930
9: 4916
10: 5014
疑似乱数ジェネレーターを新しいクラスに置き換えたいとします。上記の例では Array#shuffle が Random#rand を使用しているように見えるため、シャッフルの RNG として機能する新しいクラスを実装するのは簡単に思えます。ここでは、実際には非常に単純なラッパーである新しい疑似乱数ジェネレーターを実装しますrand
。
deck = (1..10).to_a
counts = Hash.new(0)
class FooRandom
def rand(max=nil)
max.nil? ? Kernel::rand : Kernel::rand(max)
end
end
rng = FooRandom.new
50000.times do
counts[deck.shuffle(random: rng).first] += 1
end
1.upto(10) do |card|
puts "#{card}:\t#{counts[card]}"
end
ただし、これは期待どおりに動作しません。FooRandom#rand
が呼び出されますが、シャッフルにより次の分布が生成されます。
1: 0
2: 5423
3: 5562
4: 5544
5: 5512
6: 5569
7: 5535
8: 5595
9: 5524
10: 5736
ご覧のとおり、配列がシャッフルされた後、配列値 1 が配列の最初の位置に表示されることはありません。誰でも理由がわかりますか?