If you have ever tried to get a random integer in JavaScript, you have probably been frustrated with converting the method Math.random
results (values between 0 and slightly less than 1) to integers. There are several gotchas when converting the floating points from Math.random
, in JavaScript, to integers. Todays article will walk through how to build a proper random integer generator and each gotcha you might run into.
On a first pass, one would probably come up with a method like the following:
Example 1: Math.RadomInteger() Version 1
Math.RandomInteger = function(n) { var i = Math.random(); var num = Math.round(i * n); return num; };
For all RandomInteger methods that we discuss today, the parameter n
is the upper range of the random number, so if you want number 1 through 4, then n
would equal 4. There are two issues with this version of the method: 1) the round
method will sometimes round down to 0 causing a distribution of 0 through 4, and 2) the numbers will distribute themselves more frequently to integers 1 through 9. Here is the distribution you can expect from this method (for 1000 tries and digits 1 through 10):
Example 2: Math.RadomInteger() Version 1 Distribution
0 = 36 1 = 118 2 = 93 3 = 100 4 = 108 5 = 99 6 = 99 7 = 109 8 = 98 9 = 96 10 = 44 11 = 0
A random integer generator should have an equal distribution between all desired integers (so values 1 through 10 should each have occurred about 100 times). As you can see, ZERO has been returned and ten has too low of a distribution. We fix that by adding 1 to the number returned by multiplication of the random value and n
:
Example 3: Math.RadomInteger() Version 2
Math.RandomInteger = function(n) { var i = Math.random(); var num = Math.round(i * (n - 1) + 1); return num; };
Example 4: Math.RadomInteger() Version 2 Distribution
0 = 0 1 = 52 2 = 97 3 = 116 4 = 95 5 = 128 6 = 111 7 = 113 8 = 114 9 = 122 10 = 52 11 = 0
Now we are returning the correct integers (only 1 through 10), but both ten and one have the wrong distribution (about half of what they should). The problem is using Math.round
causes ten and one to loose about 50% of their values, because half rounds down for 9 < x < 9.5 and up for 1.5 <= x < 2. Instead we should use Math.floor
or Math.ceil
, which will consider all values between two integers to round to only 1 integer, fixing the distribution:
Example 5: Math.RadomInteger() Version 3
Math.RandomInteger = function(n) {
var i = Math.random();
var num = Math.ceil(i * n);
return num;
};
Example 6: Math.RadomInteger() Version 3 Distribution
0 = 0 1 = 91 2 = 112 3 = 103 4 = 82 5 = 105 6 = 99 7 = 112 8 = 110 9 = 99 10 = 87 11 = 0
This distribution looks great, so we are done, right? Well, no, because as I mentioned above, Math.random
returns ZERO through slightly less than 1. There is a very, very small chance that ZERO will be returned (although, out of several thousand tries, I was never able to get ZERO, you still want to prevent it). So, instead of using the Math.ceil
, we should use Math.floor
on the value we rounded in Example 3. Here is the final method:
Example 7: Math.RadomInteger() Final Version
Math.RandomInteger = function(n) { var i = Math.random(); var num = Math.floor(i * n + 1); return num; };
Example 8: Math.RadomInteger() Final Version Distribution
0 = 0 1 = 96 2 = 114 3 = 83 4 = 108 5 = 105 6 = 98 7 = 97 8 = 106 9 = 93 10 = 100 11 = 0
This is a pretty good distribution, and there is no chance of getting either ZERO or 11. So, this is the correct function. However, lets make one final improvement, allowing you to pass both a maximum and minimum value, returning a random integer in that range, inclusive of the end points.
Example 9: Math.RadomInteger() Max & Min Version
Math.RandomInteger = function(n, m) { if (! m) {m = 1;} // default range starts at 1 var max = n > m ? n : m; // doesnt matter which value is min or max var min = n === max ? m : n; // min is value that is not max var d = max - min + 1; // distribution range return Math.floor(Math.random() * d + min); };
Example 10: Math.RadomInteger() Max & Min Version (range 6-10)
0 = 0 1 = 0 2 = 0 3 = 0 4 = 0 5 = 0 6 = 197 7 = 203 8 = 204 9 = 215 10 = 181 11 = 0
Note that the 1000 values are nearly evenly distributed through six and ten. This method also supports passing just a single value (the maximum) and working as the Example 8, ranging between 1 and the maximum. I have also created a Math.RandomInteger test page, if you want to see the distributions yourself. Reload the page to have it compute the values again.
I have also included a new Math.js which I will use to augment the JavaScript native Math Object in the future.
--------------
A commenter pointed out that this method isnt the most efficient/effective random number generator available. He is correct, this is just the simplest and most easy to understand version. If you want a better random number generator then check out Mersenne Twister (MT), which has already been converted to JavaScript.