Israel Cruz

A developer's blog

Random

Take a look at the following puzzle:

"Given a function which produces a random integer from 1 to 5, write one that produces a random integer from 1 to 7"

I immediately thought of this:

function naiveRand(f){
 return (f() + f()) % 7 + 1;
}

But it is not random enough, since we have more chances of getting 7 than any other number; with these two executions of f, each one producing an interger from 0 to 5 we have 25 possibilities:

1 : 4 chances: 5+2, 2+5, 3+4, 4+3
2 : 3 chances: 3+5, 5+3, 4+4
3 : 3 chances: 4+5, 5+4, 1+1
4 : 3 chances: 1+2, 2+1, 5+5
5 : 3 chances: 3+1, 1+3, 2+2
6 : 4 chances: 1+4, 4+1, 2+3, 3+2
7 : 5 chances: 5+1, 1+5, 4+2, 2+4, 3+3

Or check it in real life:

Created with Highcharts 4.0.3naiveRandexecuted 1 million timesnaiveRand12345670k50k100k150k200k250k

We can see that this results are not uniform there is always this peak on the right, so in my attempt to flatten out this I added a few more executions of f():

function newRand(){
 return (
   f() +
   f() +
   f() +
   f() +
   f() +
   f() 
 ) % 7 + 1;
}
Created with Highcharts 4.0.3newRandexecuted 1 million timesSeries 112345670k50k100k150k200k

The number of combinations is 5^6 (15625) and as you can see in the graphic above, it seems pretty flat, but if you count the number of chances of geting each integer from 1 to 7 you'll find this:

1 : 2229
2 : 2224
3 : 2224
4 : 2229
5 : 2238
6 : 2243
7 : 2238

Number 6 is slightly more likely to come out. So how could this task be achieved? I spent around an hour trying to think of a better solution, but I couldn’t. Surfing the web I found a couple of different approaches, none of them was good enough until I found Simon's, which I think is more elegant and effectively random, or at least as random as the given function should be:

function simonsRand(){
 var randtab = [
   [1,1,1,2,2],
   [2,3,3,3,4],
   [4,4,5,5,5],
   [6,6,6,7,7],
   [7,0,0,0,0]
 ];
 var r = 0;
 while(r == 0)
   r = randtab[ f() -1][ f() -1];
 return r;
}

By creating this table, he uses just two executions of f() to produce a [i,j] position. The table contains each integer from 0 to 7 exactly 3 times which is the best way to fill the table with minimum wasted spaces, if it gets 0, just repeat. We end up with 1/25 possibilities of hitting each position.

Created with Highcharts 4.0.3FinallyAll previous functions executed 1 million timesnaiveRandnewRandsimonsRand12345670k50k100k150k200k250k