Math.round()
vs. Math.floor()
The first thing to note: Math.round()
is never the right function to use when you're dealing with a value returned by Math.random()
. It should be Math.floor()
instead, and then you don't need that -1
correction on the length
. This is because Math.random()
returns a value that is >= 0
and < 1
.
This is a bit tricky, so let's take a specific example: an array with three elements. As vihan1086's excellent answer explains, the elements of this array are numbered 0
, 1
, and 2
. To select a random element from this array, you want an equal chance of getting any one of those three values.
Let's see how that works out with Math.round( Math.random() * array.length - 1 )
. The array length is 3
, so we will multiply Math.random()
by 2
. Now we have a value n
that is >= 0
and < 2
. We round that number to the nearest integer:
If n
is >= 0
and < .5
, it rounds to 0
.
If n
is >= .5
and < 1.5
, it rounds to 1
.
If n
is >= 1.5
and < 2
, it rounds to 2
.
So far so good. We have a chance of getting any of the three values we need, 0, 1, or 2. But what are the chances?
Look closely at those ranges. The middle range (.5
up to 1.5
) is twice as long as the other two ranges (0
up to .5
, and 1.5
up to 2
). Instead of an equal chance for any of the three index values, we have a 25% chance of getting 0
, a 50% chance of getting 1
, and a 25% chance of 2
. Oops.
Instead, we need to multiply the Math.random()
result by the entire array length of 3
, so n
is >= 0
and < 3
, and then floor that result: Math.floor( Math.random() * array.length )
It works like this:
If n
is >= 0
and < 1
, it floors to 0
.
If n
is >= 1
and < 2
, it floors to 1
.
If n
is >= 2
and < 3
, it floors to 2
.
Now we clearly have an equal chance of hitting any of the three values 0
, 1
, or 2
, because each of those ranges is the same length.
Keeping it simple
Here is a recommendation: don't write all this code in one expression. Break it up into simple functions that are self-explanatory and make sense. Here's how I like to do this particular task (picking a random element from an array):
// Return a random integer in the range 0 through n - 1
function randomInt( n ) {
return Math.floor( Math.random() * n );
}
// Return a random element from an array
function randomElement( array ) {
return array[ randomInt(array.length) ];
}
Then the rest of the code is straightforward:
var examples = [ 1, 2, 3, 56, "foxy", 9999, "jaguar", 5.4, "caveman" ];
var example = randomElement( examples );
console.log( example );
See how much simpler it is this way? Now you don't have to do that math calculation every time you want to get a random element from an array, you can simply call randomElement(array)
.