33

I have all kinds of resources that rely on javascript random numbers. However, I've been seeing a lot of problems where random isn't so random because of the way I'm generating random numbers.

Is there any javascript resource for me to generate true, or just better random numbers?

I know that I can interface with Random.org, but what other options do I have?

I'm using:

function rand( lowest, highest){
    var adjustedHigh = (highest - lowest) + 1;       
    return Math.floor(Math.random()*adjustedHigh) + parseFloat(lowest);
}
rlb.usa
  • 14,942
  • 16
  • 80
  • 128
  • In what way are they not so random? i.e. how do you know? – Phil H Oct 01 '12 at 12:56
  • What do you mean by "true random"? Afaik you can't *generate* them with an algorithm, so you will need to proxy http://www.random.org/clients/http/ – Bergi Oct 01 '12 at 13:07
  • @PhilH http://boallen.com/random-numbers.html – rlb.usa Oct 01 '12 at 14:35
  • 1
    rlb.usa, that link concerns PHP random numbers. For javascript, I suspect the randomness of the PRNG will be dependent on the implementation; have you tested the PRNG you are using? Again, how do you know it's not good enough? – Phil H Oct 01 '12 at 14:47
  • Related: https://stackoverflow.com/questions/9550796/why-is-google-chromes-math-random-number-generator-not-that-random – Cees Timmerman Apr 24 '20 at 14:30

7 Answers7

23

Assuming you're not just seeing patterns where there aren't any, try a Mersenee Twister (Wikipedia article here). There are various implementations like this one on github.

Similar SO question:

Seedable JavaScript random number generator

If you want something closer to truly random, then consider using the random.org API to get truly random numbers, although I would suggest only using that to seed, not for every number, as you need to abide by their usage limits.

Community
  • 1
  • 1
Phil H
  • 19,928
  • 7
  • 68
  • 105
21

Tweaking numbers so they "look random"

I agree with Phil H that humans are so good at finding patterns that they often think they see patterns even in "perfectly random" sequences of numbers (clustering illusion, apophenia, gambler's fallacy, etc).

Plots of true random positions generally have lots of clumps and points that "coincidentally" fall very close together, which looks pretty suspicious.

Artists often take completely randomly generated patterns and "nudge" them to make them appear "more random", even though that careful nudging actually makes the pattern less random (a), (b), (c), (d), etc.

Alternatively, a low-discrepancy sequence sometimes "looks better" than a true random sequence and is much faster to generate.

Fast random number generators

There are many "random number generators" across a whole spectrum from "extremely fast" to "relatively slow" and from "easy for even a human to see patterns" to "unlikely that unassisted humans could ever see any patterns" to "cryptographically secure and, after seeded with adequate amounts of entropy, as far as we can tell, indistinguishable from random to any attacker using less than all the energy produced by humanity for a month."

Non-cryptographic-strength random number generators that still give excellent output (unlikely that unassisted humans could ever see any patterns) include the Mersenne twister, multiply-with-carry, Lagged Fibonacci generator, Well equidistributed long-period linear, Xorshift, etc.

Cryptographic random number techniques that work with some browsers

I hear that Cryptocat and other JavaScript applications use the convenient window.crypto.getRandomValues() or window.msCrypto.getRandomValues() or SubtleCrypto.generateKey() functions that are designed to generate cryptographic random numbers. Unfortunately, that function is not available in IE 11 and below.

Since web browsers use random numbers all the time (for every "https://" page they fetch), it's quite likely that these functions (where available) may run faster than most random number generators written in JavaScript -- even non-cryptographic algorithms.

Cryptographic random number techniques compatible with ancient and modern browsers

One way to generate true random numbers in JavaScript is to capture mouse events and add them into a pool of entropy, keeping track of some (hopefully conservative) estimate of the entropy added. Once the pool is "full" (estimates indicate that at least 128 bits of entropy have been added), use some cryptographically secure random number generator to generate random numbers from the pool -- typically by using a one-way hash so that a sequence of a few thousand output numbers are not enough to deduce the state of the entropy pool and hence predict the next output number.

One implementation: http://lightsecond.com/passphrase.html

Further reading

Joshua Fan
  • 237
  • 2
  • 7
David Cary
  • 5,250
  • 6
  • 53
  • 66
  • One idea is to create an array of entries that have already been pick by random. Then the function returning new random items would ignore those already chosen once and give the turn to items that haven't been picked yet. Until all items have been picked at least once. – Azevedo Jul 28 '19 at 18:27
7

While looking for an alternative for Math.random I stumbled on this question.

While those are valid answers, the solution that worked for me was simply using Math.random twice.
And use a modulus on the decimals of the float.
Basically to increase the randomness.

Maybe it might be usefull for some who were guided by google to this question.

Here's a snippet with the function, and one that runs it a million times.

function rand(min, max){
    return (Math.floor(Math.pow(10,14)*Math.random()*Math.random())%(max-min+1))+min;
}

// rolling the rand
function rollRands(min, max, rolls) {
    let counts = {};
    for(let i = min; i <= max; i++) {
        counts[i] = 0
    }
    let roll = 0;
    while (roll < rolls) {
        counts[rand(min,max)]++;
        roll++;
    }
    return counts;
}

// testing a million rands
console.log(rollRands(36, 42, 1000000));
LukStorms
  • 28,916
  • 5
  • 31
  • 45
5

Rando.js is cryptographically secure. It's basically window.crypto.getRandomValues() that uses window.msCrypto.getRandomValues() as a failsafe and Math.random() as a last resort failsafe, but it's easier to implement and use. Here's a basic cryptographically secure random [0, 1) number:

console.log(rando());
<script src="https://randojs.com/2.0.0.js"></script>

Nice and easy. If that's all you wanted, you're good to go. If you want it to do more for you, it's also capable of all this:

console.log(rando(5));                      //an integer between 0 and 5 (could be 0 or 5));  
console.log(rando(5, 10));                  //a random integer between 5 and 10 (could be 5 or 10));  
console.log(rando(5, "float"));             //a floating-point number between 0 and 5 (could be exactly 0, but never exactly 5));  
console.log(rando(5, 10, "float"));         //a floating-point number between 5 and 10 (could be exactly 5, but never exactly 10));  
console.log(rando(true, false));            //either true or false  
console.log(rando(["a", "b"]));             //{index:..., value:...} object representing a value of the provided array OR false if array is empty  
console.log(rando({a: 1, b: 2}));           //{key:..., value:...} object representing a property of the provided object OR false if object has no properties  
console.log(rando("Gee willikers!"));       //a character from the provided string OR false if the string is empty. Reoccurring characters will naturally form a more likely return value  
console.log(rando(null));                   //ANY invalid arguments return false  

//Prevent repetitions by grabbing a sequence and looping through it
console.log(randoSequence(5));              //an array of integers from 0 through 5 in random order  
console.log(randoSequence(5, 10));          //an array of integers from 5 through 10 in random order  
console.log(randoSequence(["a", "b"]));     //an array of {index:..., value:...} objects representing the values of the provided array in random order  
console.log(randoSequence({a: 1, b: 2}));   //an array of {key:..., value:...} objects representing the properties of the provided object in random order  
console.log(randoSequence("Good gravy!"));  //an array of the characters of the provided string in random order  
console.log(randoSequence(null));           //ANY invalid arguments return false
<script src="https://randojs.com/2.0.0.js"></script>

It supports working with jQuery elements too, but I left that out of this demo so I wouldn't have to source in jQuery. If you need that, just check it out on the GitHub or website.

Aaron Plocharczyk
  • 2,776
  • 2
  • 7
  • 15
  • this is the best, sweetest, most updated answer so far... only missing to mention that `window.crypto` is now basically standard everywhere (to counter the other outdated answers) and it uses true source of entropy everywhere, which were all made common by `/rev/urandom` https://w3c.github.io/webcrypto/#crypto-interface (i also want to just mention a side agenda of mine here, and a huge issue: both github and rando.js use licenses and/or do practices that are unhealthy to freedom, imho. look it up: only fsf #agpl save! ) – cregox Dec 29 '21 at 07:09
  • rando.js uses the "unliscence". you're free to do whatever you want with it, commercially or otherwise without the need to give credit in any way. do what you want with it :) – Aaron Plocharczyk Dec 29 '21 at 18:33
  • you haven't looked it up, aaron... here, i truly hope this might be a better starting point for you: https://banana.dog/@cregox/107140241290340311#agpl notice it is using mastodon, one of the very few pieces of code online today that uses agpl. freedom is a very complex topic: it have little to do with "doing whatever you want" and everything to do with "doing what's best for everyone". more choice can mean less freedom. zero choice can mean more freedom. choice is nothing but an illusion. licenses work with legalities and the bureaucracy of freedom, and differ very much from law enforcement. – cregox Jan 05 '22 at 15:31
  • This is getting too philosophical for me. As long as my point has gotten across that rando.js does not intend to punish anybody in any way for using its code for any purpose, I'll leave the rest of your point for others to dissect. Thanks for your input. If you care to leave more notes about your topic here, feel free. – Aaron Plocharczyk Jan 05 '22 at 23:42
  • thank you too! last note: philosophy united with religion and science is what builds our reality as it is. google only forbids agpl. google silently removed "don't be evil" from their manifesto many years ago. you do the math. – cregox Jan 11 '22 at 09:12
4

There seems to be slight confusion here between two things that are very different:

  • random numbers;
  • pseudorandom numbers.

Apologies to those who know this already, but the two are worlds apart. Pseudorandom numbers appear random and may even pass sophisticated tests of randomness, but they are deterministic. Because of this, they are useless for cryptography, and may have other flaws where true randomness is required.

True randomness is non-deterministic, and thus unpredictable. A key concept here is one of entropy, or the amount of non-redundant information contained. There is a limited number of ways of obtaining truly random data. 'Good' sources are:

  1. Radioactive decay — often difficult to do;
  2. Background radio noise — contrary to popular believe, this is mostly not related to the background microwave radiation from the big bang, but more parochial;
  3. The noise from electrons moving across a reverse-biased Zener diode: actually quite useful and easy to implement in practice, with simple circuitry.

Other 'sources of randomness' like mouse movements and internal variations in computer disk timing, etc. are often harnessed, but may be less-than-perfect. Generally, I've found that it's easier to access the entropy pool on a Linux system than under Windows, but this may just be personal bias.

If you just want random-appearing numbers, then yes, using the Mersenne twister is a viable option. It jumps around like crazy. If you're generating random numbers to use as e.g. version 4 UUIDs, then you need to be more careful. You can't simply 'add entropy' if it's not there, even by applying deterministic cryptographic functions.

If you intend to use your randomness for cryptography, you should also be intensely aware of the many ways your source of randomness can become compromised. For example, if you're using an Internet-based 'source of randomness', who can tap into this?

Pierre.Vriens
  • 2,117
  • 75
  • 29
  • 42
  • The question is about acquiring true random numbers in *JavaScript*. The answers mostly serve up pseudorandom. This is difficult. Good luck in accessing /dev/urandom from JavaScript (I used to be able to do this with NPAPI, now defunct). Another approach with a functioning circuit diagram: https://www.quora.com/What-is-the-difference-between-a-random-and-a-pseudo-random-sequence/answer/Dr-Jo-6 ; in JavaScript you would need to use the Web Audio API. – Jo van Schalkwyk Dec 30 '21 at 09:36
  • do you happen to know why i got a notification from your comment? did i have a comment before yours that was removed?! in any case, you can read in other answers: `window.crypto` already uses `/dev/urandom` on javascript. – cregox Jan 05 '22 at 15:37
3

Even better, you can use quantum cryptography to generate randomness that is very hard to predict. You can use the ANU Quantum Random Numbers API for some randomness that is coercible into a number similarly output by Math.random.

Richie Bendall
  • 7,738
  • 4
  • 38
  • 58
2

you can generate a pool of random numbers just by requesting some data asynchronously because performance.now() gives you time precision up to microseconds. Then use the response time as a salt in a randomising algorithm,

var randomNumbers = [];
for(var i = 0; i < 10; i++) {
  setTimeout(function () {
    var timeStart = performance.now();
    xhttp = new XMLHttpRequest();
    xhttp.open('GET', 'https://cdn.polyfill.io/v2/polyfill.min.js?rand=' + Math.random(), true);
    xhttp.onload = function () {
      var timeEnd = performance.now() - timeStart;
      var rNumber = parseInt(timeEnd.toString().replace('.', ''));
      randomNumbers.push(rNumber)
    };
    xhttp.send();
  }, i * 10);
}

There are many factors that will affect this time:

  • browser speed
  • route one way
  • server response time
  • route back

It's not good to generate millions of numbers this way but a few. Maybe concatenate a few results to get a good, long random number.

Pawel
  • 16,093
  • 5
  • 70
  • 73