使用自定义随机数生成器与Ruby Array #shuffle / sample

使用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 ,但是shuffling会产生以下分布:

 1: 0 2: 5423 3: 5562 4: 5544 5: 5512 6: 5569 7: 5535 8: 5595 9: 5524 10: 5736 

如您所见,在数组被混洗后,数组值1永远不会出现在数组的第一个位置。 任何人都知道为什么?

Ruby 2.0.0p0中存在一个错误,其中限制为1。

这已在Ruby 2.0.0p195中修复,因此您应该升级您的安装。

在ruby 1.9.3中,max是零。 在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; 

}