Using custom random number generators with Ruby Array#shuffle/sample

ruby

Solution

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.

Problem

When using Array#shuffle, Ruby allows the use of a custom randomizer and even provides class `Random` to utilize it. The following example uses the class with a seed value of 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] ``` I wrote a small monobit test to see how many times a value appeared first in the shuffled array. ``` 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 ``` The output looks similar to the following: ``` 1: 4942 2: 5100 3: 4938 4: 4960 5: 5024 6: 4992 7: 5184 8: 4930 9: 4916 10: 5014 ``` Suppose I want to replace the pseudo-random number generator with a new class. Since Array#shuffle appears to use Random#rand in the above example, it would seem straightforward to implement a new class to act as the RNG for shuffling. Here, I implement a new pseudo-random number generator that actually is just a very simple wrapper around `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 ``` This, however, does not operate as expected. `FooRandom#rand` is called, but the shuffling produces the following distribution: ``` 1: 0 2: 5423 3: 5562 4: 5544 5: 5512 6: 5569 7: 5535 8: 5595 9: 5524 10: 5736 ``` As you can see, the array value 1 never appears in the first position of the array after the array is shuffled. Anyone have an idea why?

Original source