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?