使用自定义随机数生成器与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;
}