DEV Community

Cover image for So, Random
arindavis
arindavis

Posted on

So, Random

Let's go ahead and rip this band-aid off: JavaScript's Math.random() number method isn't actually random. The number it calculates could, with enough pre-emptive knowledge, be predicted. This isn't a fatal oversight on the behalf of the language's creators, it's actually a problem with how our machines and the software we design for them are taught to think in the first place.

In order to fully understand how Math.random() works, we are going to have to talk about its basic use cases, what's going on behind the scenes when we call it, and the nature of chaos within a deterministic universe.

First, let's get to know the method itself before we expose it for the no good liar it is. A simple invocation of the base method, like so--

let random = Math.random();
console.log(random)  
Enter fullscreen mode Exit fullscreen mode

--will log a number somewhere between 0-1 to the console. Try it for yourself if you'd like, I got "0.3525815464578914".

If we add the Math.floor method with a little extra flavor, however, we can make the forces of chaos truly bend to our will.

let controlledChaos = Math.floor(Math.random() * 10 + 1);
console.log(controlledChaos);
Enter fullscreen mode Exit fullscreen mode

I got 6!

We are specifying here that our random number should be strictly chosen within the range of one through ten. Math.floor() is doing some of the heavy lifting, as it's primary job is rounding to the lowest whole number. We then multiply our invocation of Math.random() by ten, setting the upper limit of our random call to ten. Then on to the final part, '+ 1', which is setting the lower limit to one. If we were to leave those last two off, like so--

let rando = Math.floor(Math.random());
console.log(rando);
Enter fullscreen mode Exit fullscreen mode

we would get zero. No matter what.
Every. Time.

This is because as Math.random() generates a random number between zero and one on each invocation, Math.floor() is simultaneously taking that number and rounding it down to zero. You could do the same thing with Math.ceil(), the sibling of .floor(), and you'd get one instead because it rounds up to the nearest whole number. It's like the JavaScript equivalent of a rigged coin toss.

Using the bones of the controlledChaos variable, we can work to make our JavaScript programs more dynamic by creating random-number generating variables that depend on/refer to other parts of our program.

Here's an example, it's a link to a rudimentary story generator I made using the above principles. Press run a couple times and see what kind of harrowing tales you get, then check out the code at the bottom that makes it all tick.

Did you notice that you can add to any of the three story sections because the upper ceiling of the random call is dynamically linked to the length property of the corresponding array? Of course you did, because you're smart.

And because I now know how smart you are, I feel comfortable getting into more of the technical side of things. Wanna know what's actually going on behind the scenes with Math.random()? The answer is complicated and would require its own blog to be properly explained, but for the sake of brevity:

Most browsers(yes, browsers implement this method, not JavaScript) use something called a "Pseudo-Random Number Generator" to simulate randomness in our code. One of the most popular PRNGs regularly implemented with JavaScript is called "xorshift128+", which you can read more about here and here.

In short, every PRNG needs a place to start, a seed number from which all the following numbers can be subsequently calculated. That's why most PRNG's rely on external means of getting a seed, like from your computer's internal clock, the current heat of your CPU, mouse movements, fan noise, or even the radiation levels in your immediate area. Alright, that last one was a little extreme.

I'd encourage you to do some more research on your own time starting with the provided links, because my brief explanations of the inner workings of these PRNGs only scratch the surface of what there is to know, and we aren't even touching on other topics related to randomization like HRNGs or whacky projects like this one.

But here's the run-down on PRNGS: There is nothing actually random about how most of them get their end results, even ones that are used as the standard, because every end value was calculated by a deterministic process. That's where the pseudo part of that really cool name comes from.

What most of them actually do is simulate randomness for us by starting with a seed and unloading a predetermined series of processes onto it. If one were to know the exact inner workings of that process and the seed itself, one could, theoretically, predict what the software will return before it returns it. This is immensely impractical and absolutely the most unnecessary flex imaginable, but it's also technically possible, which is the best kind of possible. It's also why I argue that Math.random() is inherently chaotic and not random. Because, given you have accurate information about the input, you could accurately predict the output no matter the complexity. This is how nearly all man made creations work, as deterministic systems.

But what does "random" even really mean, if not Chaos?

When you get together with your nerdy DND friends to tell stories and roll funny shaped dice, are the results from those funny shaped dice truly random? The dice's output may seem obviously randomized on the surface, but one could argue that the end value is determined by tangible conditions like density and balance of the dice, how the user consciously and subconsciously decides to throw it, which part of the table it lands on, the balance of the house and so on...

Which leaves us with no clear answer and one lingering question:

If it looks random when we see it, feels random when we use it, and acts random when we need it to then does it even matter if Math.random() is factually "random" or not?

Top comments (0)