Make JavaScript Math.random() useful

Posted Aug 3, 2004
Last Updated Nov 7, 2011
The JavaScript function Math.random() randomly generates a number from 0 to slightly less than 1 (shown as <1). This is great, and all, but when was the last time someone told you to pick a number between 0 and slightly less than 1? Never? How do we transform these numbers into something useable, say a whole number between 1 and 3. (One and ten are more common, but the illustrations would be too huge.) First, we could multiply. Let’s try multiplying by 3.

Math.random() * 3

This gets us numbers from 0 to <3. But we want between 1 and 3. We’ll try multiplying and adding.

Math.random() * 2 + 1

Great. This will get us numbers from 1 to <3. (First it generates a number from 0 to <2, then adds 1.) Lets see how this looks on a number line:
1 1.5 2 2.5 3 
|-----|-----|-----|-----|

Good. But this still gives us decimal numbers such as 1.56734. We want whole number. So naturally we should round. Lets try:

Math.round(Math.random*2+1)

This will produce whole numbers between 1 and 3 inclusive. However, the numbers are perfectly random. To be perfectly random each number should appear the same number of times, but in this case the number 2 will appear most often. Let’s put this on a number line. Recall from elementary school that things <.5 round down and >.5 round up.
1 1.5 2 2.5 3 
|-----|-----|-----|-----|
1<<<<< >>>>>2<<<<< >>>>>3

So as you can see, the segment that rounds to two is twice as long as the segments that round to one or three.

The fix

How about we change our math a little and always round up. The function we need is called the ceiling function because it rounds a number up to the "ceiling.”

Math.ceil(Math.random()*3)

Going to the number line:
0 .5 1 1.5 2 2.5 3 
|-----|-----|-----|-----|-----|-----|
0>>>>>>>>>>>1>>>>>>>>>>>2>>>>>>>>>>>3

Awesome. However, (oh no, not again) remember how the Math.random() function gives us numbers from 0 to <1? That mean there is a super-slim chance of getting exactly 0. Math.ceil(0) equals 0. So there is a slim chance we will get 0 as a result.

The real fix

We don’t have to round to get integers. We can truncate. This means that we just throw away anything after the decimal point. OK, so this is technically the same as always rounding down, but whatever. This function is called the floor function.

Math.floor(Math.random()*3+1)

Lets go through it. First we get a number between 0 and <3 and then add 1 to it. This gives us 1 to <4. Remember that slightly less than 4 could be 3.99999 but it will never be 4. So when we floor the number we will always get a whole number between 1 and 3 inclusive. Check out the number line:

1 1.5 2 2.5 3 3.5 4
|-----|-----|-----|-----|-----|-----|
1<<<<<<<<<<<2<<<<<<<<<<<3<<<<<<<<<<<

In conclusion, random numbers are only purely random if you transform them correctly. Otherwise you get skewed and unexpected results. Use:

Math.floor(Math.random()*max+1)

to get numbers between 1 and max. Homework: make a function to get numbers between min and max. Play around with these functions at: ThePenry.net Math.random() page (temporarily down). 

Knowledge is power. Power corrupts. You are now more corrupted.

Comment

No HTML Tags are permitted.

Bob

Mar 28, 2013

I dispute the following: "To be perfectly random each number should appear the same number of times..."
Random means there no way to predict the next event and not equal distribution. For instance, a coin flip is a random event. If you flip a fair coin 100 times & you get heads all 100 times (a highly unlikely but possible outcome) what are the odds of getting heads on toss #101?
Your claim of equal distribution would mean that you would almost certainly get tails on toss #101 to start evening out the distribution. Whereas, the actual chance for getting heads on toss #101 is still 0.5.

vincent piel

Nov 24, 2012

the REAL fix : only floor the 'scaled' random(), not the initial value :
in your example : 1+Math.floor(3*Math.random())
general case is : firstIndex + ( Math.floor( ( lastIndex - firstIndex + 1 ) *Math.random() )

nice anyway, because if your input is allready a 'float' number (expl : 5.4256) random between
n and p (integers expl : between 4 and 10 ) and that you want to use it to find an array index you have to think about using
ceil and +0,5.

Comment awaiting approval.

Comment awaiting approval.

Comment awaiting approval.

Comment awaiting approval.

Comment awaiting approval.

Comment awaiting approval.

Sky Writer