Thursday, 19 November 2009

Don't take your random() for granted...

In my current project, we use a functionality which chooses a random colour for a machine. To do this, we need to select a colour from a set of 8 colours. In our Flex application we use a function called randomInt, which generates a random integer between a and b:

public static function randomInt(a:int, b:int):int {
return Math.round(Math.random()*(b-a))+a;
}


Usually we look for functions like these on the internet, but I’d like to emphasize that you also should unit-test functions which are made by others.

So I’ve generated 5000 integers between 0 and 20 in a test application, and put that in a graph:



Note that the numbers on the boundaries, 0 and 20, are significantly chosen less times than the others (1-19). This would mean, that if this function is used for picking a random machine colour, the colours 0 and 7 have only a 7% chance to be selected, while others have a 14% chance!

I’ve talked about this to someone who knows a lot of these mathematical things. The problem is the rounding. Since Math.round() returns a pseudo-random number n, where 0 <= n <>
We need to include the boundaries to produce a evenly distributed random integer. This is the new function:
private function randomInt(a:int, c:int):int {
var b:int = c + 1;
return Math.floor(Math.random()*(b-a))+a;
}


Doing the same test, produces this graph:



As you can see, each integer is chosen about the same number of times. Now we know for sure that colours 0 and 7 won’t stay in storage, but are also sold!