True or better Random numbers with Javascript

2020-01-25 07:29发布

问题:

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);
}

回答1:

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.



回答2:

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

  • window.crypto
  • Compatibility of window.crypto.getRandomValues()
  • Secure random numbers in javascript?
  • https://security.stackexchange.com/questions/20029/generate-cryptographically-strong-pseudorandom-numbers-in-javascript
  • Is there any built in browser support for crypto random numbers in IE and Webkit? Firefox has window.crypto
  • Better random function in JavaScript


回答3:

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.



回答4:

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;
}

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

    while (roll < rolls){
        roll++;
        counts[rand(min,max)]++;
    }
    return counts;
}
  
console.log(rollRands(36, 42, 1000000));



回答5:

I made a JavaScript library that uses cosine and sine functions to generate random numbers using Date.now() and new Date().getTime(). For each random number, I test to see if the number has been used. If it has, I repeat the process until I get a new number. If I get a new number, I add the number to the used list and then return the value. In the same library, I also added a randomness tester which loops through a random number generator and looks for patterns. It's nice this way since it loads numbers quickly (I timed it with console.time) without unnecessarily contacting other pages.

You can find the library here: punbb.atspace.cc/scripts/libraries/random.js