4

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 が配列の最初の位置に表示されることはありません。誰でも理由がわかりますか?

4

2 に答える 2

4

There is a bug in Ruby 2.0.0p0 where the limit is off by one.

This has been fixed in Ruby 2.0.0p195, so you should upgrade your installation.

于 2013-04-20T16:44:05.540 に答える
2

Ruby 1.9.3 では max は nil です。Ruby 2.0.0 では max が rand に渡されます。rand メソッドを次のように変更することで、機能させることができました。

class FooRandom
  def rand(max=nil)
    max.nil? ? Kernel::rand : Kernel::rand(max+1)
  end
end

前は一つずれていました。

これは、 http://ruby-doc.org/core-2.0/Array.html#method-i-shuffleからの C ソースの抜粋です 。

i = RARRAY_LEN(ary);
ptr = RARRAY_PTR(ary);
while (i) {
    long j = RAND_UPTO(i);
    VALUE tmp;
    tmp = ptr[--i];
    ptr[i] = ptr[j];
    ptr[j] = tmp;
}
return ary;

}

于 2013-04-20T08:42:12.617 に答える