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

When using Shuffle Array # Ruby allows you to use a custom randomizer and even provides a Random class to use it. The following example uses a class with an initial 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 one-bit test to find out how many times the 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 result looks something like this:

 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 seems to be using Random # rand in the example above, it would be easy to implement a new class to act as an RNG for shuffling. Here I am implementing a new pseudo-random number generator, which is actually just a 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 work as expected. FooRandom#rand is called, but shuffling creates 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 value of array 1 never appears in the first position of the array after shuffling the array. Does anyone have an idea why?

+4
source share
2 answers

There is an error in Ruby 2.0.0p0 where the restriction is disabled by one.

This is fixed in Ruby 2.0.0p195, so you must upgrade your installation.

+4
source

In ruby ​​1.9.3 max is zero. In Ruby 2.0.0 max, rand is passed. I was able to get it working by changing the rand method to the following.

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

This was previously disabled.

Here is an excerpt from source C from http://ruby-doc.org/core-2.0/Array.html#method-i-shuffle

 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; 

}

+2
source

All Articles