Math.random in regards to arrays
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)
.
They're is quite a bit happening so I'll break it up:
Math.random
You got the first part right. Math.random
will generate a number >= 0 and < 1. Math.random
can return 0 but chances are almost 0 I think it's like 10^{-16}
(you are 10 billion times more likely to get struck by lightning). This will make a number such as:
0.6687583869788796
Let's stop there for a second
Arrays and their indexes
Each item in an array has an index or position. This ranges from 0 - infinity. In JavaScript, arrays start at zero, not one. Here's a chart:
[ 'foo', 'bar', 'baz' ]
Now the indexes are as following:
name | index
-----|------
foo | 0
bar | 1
baz | 2
To get an item from it's index, use []
:
fooBarBazArray[0]; // foo
fooBarBazArray[2]; // baz
Array length
Now the array length won't be the same as the largest index. It will be the length as if we counted it. So the above array will return 3
. Each array has a length
property which contains it's length:
['foo', 'bar', 'baz'].length; // Is 3
More Random Math
Now let's take a look at this randomizing thing:
Math.round(Math.random() * (mathematics.length-1))
They're is a lot going on. Let's break it down:
Math.random()
So first we generate a random number.
* mathematics.length - 1
The goal of this random is to generate a random array index. We need to subtract 1 from the length to get the highest index.
First Part conclusions
This now gives us a number ranging from 0 - max array index. On the sample array I showed earlier:
Math.random() * (['foo', 'bar', 'baz'].length - 1)
Now they're is a little problem:
This code makes a random number between 0 and the length. That means the -1
shouldn't be there. Let's fix this code:
Math.random() * ['foo', 'bar', 'baz'].length
Running this code, I get:
2.1972009977325797
1.0244733088184148
0.1671080442611128
2.0442249791231006
1.8239217158406973
Finally
To get out random index, we have to make this from an ugly decimal to a nice integer: Math.floor
will basically truncate the decimal off.
Math.floor results:
2
0
2
1
2
We can put this code in the []
to select an item in the array at the random index.
More Information / Sources
- Random Numbers
- More solutions
You're looking at simple multiplication, and a bug in your code. It should reference the array 'examples' that you are selecting from, instead of some thing you haven't mentioned called 'mathematics':
var example = examples[Math.round(Math.random() * (examples.length-1))];
^^
Then you're just multiplying a random number by the number of things in the array. So the maximum random number is 1 and if there are 50 things in your array you multiply the random number by 50, and now the maximum random number is 50.
And all the smaller random numbers (0 to 1) are also scaled 50x and now spread from (0 to 50) with roughly the same randomness to them. Then you round it to the nearest whole number, which is a random index into your array from 1 to n, and you can do element[thatnumber] to pick it out.
Full examples:
Math.random() returns numbers between 0 and 1 (it can return 0 but chances of that are incredibly small):
Math.random()
0.11506261994225964
Math.random()
0.5607304393516861
Math.random()
0.5050221864582
Math.random()
0.4070177578793308
Math.random()
0.6352060229006462
Multiply those numbers by something to scale them up; 1 x 10 = 10 and so Math.random() * 10 = random numbers between 0 and 10.
Math.random() *
n
returns numbers between 0 and n:
Math.random() * 10
2.6186012867183326
Math.random() * 10
5.616868671026196
Math.random() * 10
0.7765205189156167
Math.random() * 10
6.299650241067698
Then Math.round(number)
knocks the decimals off and leaves the nearest whole number between 1 and 10:
Math.round(Math.random() * 10)
5
Then you select that numbered element:
examples[ Math.round(Math.random() * 10) ];
And you use .length-1
because indexing counts from 0 and finishes at length-1, (see @vihan1086's explanation which has lots about array indexing).
This approach is not very good at being random - particularly it's much less likely to pick the first and last elements. I didn't realise when I wrote this, but @Michael Geary's answer is much better - avoiding Math.round() and not using length-1.