0

I understand you can shuffle an array in JavaScript using a Fisher-Yates Shuffle. This however uses Math.random for it's random numbers. I was wondering if you could get a better shuffle using window.crypto.getRandomValues() for the random number source?

I have had a go below which works. The getRandomIntInRange() function uses rejection sampling and example specified here.

Please let me know if this is the right way to do it, or if you can think of a better way to do it. JSFiddle here.

$(document).ready(function()
{
    var dataArray = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14'];               
    var shuffledArray = shuffleArray(dataArray);                

    console.log(shuffledArray);
});         

function getRandomIntInRange(min, max) {

    var range = max - min + 1;
    var maxRange = 256;
    var byteArray = new Uint8Array(1);

    // Fill byteArray with 1 random number
    window.crypto.getRandomValues(byteArray);

    // If outside of range, get another
    if (byteArray[0] >= Math.floor(maxRange / range) * range)
    {
        return getRandomIntInRange(min, max);
    }

    return min + (byteArray[0] % range);
}

function shuffleArray(dataArray) {

    var counter = dataArray.length, temp, index;

    while (counter > 0)
    {
        index = getRandomIntInRange(0, counter - 1);
        counter--;

        temp = dataArray[counter];
        dataArray[counter] = dataArray[index];
        dataArray[index] = temp;
    }

    return dataArray;
}
Community
  • 1
  • 1
user2503552
  • 601
  • 2
  • 8
  • 14

1 Answers1

3

There's nothing wrong with this code (although using strings for the numbers 1 to 14 seems pointlessly slow and complex--what's wrong with just the numbers 1 to 14?). You're certainly free to use whatever RNG algorithm you like. But different algorithms are designed to be best suited for different tasks. Generally speaking, RNGs designed for simulations will not be secure for cryptography; cryptographically secure RNGs will be acceptable for simulations, but will probably be too slow. If you only want to play a few games, no problem. But if you want to simulate a billion hands of blackjack or poker, or do Monte Carlo integration with billions of datapoints, using a cryptographic RNG may well take your code from running in minutes to running in weeks for no benefit.

Lee Daniel Crocker
  • 12,927
  • 3
  • 29
  • 55
  • Considering the speed of a good CSPRNG there is little reason to use anything else except in really random heavy simulation, which wouldn't be written in js anyways. A single modulo operation is more expensive than generating bytes with chacha12 or AES-CTR on modern hardware. – CodesInChaos Aug 21 '13 at 16:02
  • Totally agree that worrying about performance in JavaScript might be barking up the wrong tree, but it does matter. I can run quite literally *billions* of hands of blackjack or poker in minutes in C with Marsaglia's MWC or Jones's JKISS. Even the fastest CSPRNG would push that out by an order of magnitude, because the PRNG is a significant percentage of the time taken. Yeah, if you have hardware AES, all bets are off. – Lee Daniel Crocker Aug 22 '13 at 02:03
  • The regular Math.random() deos not have enough entropy for for an actual deck of cards (52!), and you can't force a reseed to overcome that. That said, I am unsure if crypto.getRandomValues() has enough entropy, either. I can't find any data on how much it has. – trlkly Mar 26 '17 at 22:19