How to Use a Seed to Generate Random Numbers in JavaScript

Habdul Hazeez Feb 02, 2024
  1. Use a Seed and SFC32 to Generate a Random Number
  2. Use a Seed and Mulberry32 to Generate a Random Number
  3. Use a Seed and Xoshiro128** to Generate a Random Number
  4. Use a Seed and JSF to Generate a Random Number
  5. Use seedrandom.js to Generate a Random Number
How to Use a Seed to Generate Random Numbers in JavaScript

This article tackles how to use a seed to generate random numbers from PRNGs. Meanwhile, it’s a best practice to ensure that the seed for the PRNGs has high entropy.

Therefore, we’ll use a hashing function to generate the seed. Afterward, we pass the seed to the PRNG.

Use a Seed and SFC32 to Generate a Random Number

SFC32 or Simple Fast Counter is a fast PRNG from PractRand (mostly in C), and it has an implementation in JavaScript with a 128-bit state, and it’s very fast. SFC32 requires at least a single seed to generate the random number.

We’ll generate this seed using the hashing function, a JavaScript implementation of MurmurHash3, which requires an initial string to generate the seed. As a result, we pass in a string.

In the following code, we generate the seed and pass it to SFC32 that returns the random number.

Code:

// Define the Murmur3Hash function
function MurmurHash3(string) {
  let i = 0;
  for (i, hash = 1779033703 ^ string.length; i < string.length; i++) {
    let bitwise_xor_from_character = hash ^ string.charCodeAt(i);
    hash = Math.imul(bitwise_xor_from_character, 3432918353);
    hash = hash << 13 | hash >>> 19;
  }
  return () => {
    // Return the hash that you can use as a seed
    hash = Math.imul(hash ^ (hash >>> 16), 2246822507);
    hash = Math.imul(hash ^ (hash >>> 13), 3266489909);
    return (hash ^= hash >>> 16) >>> 0;
  }
}

function SimpleFastCounter32(seed_1, seed_2, seed_3, seed_4) {
  return () => {
    seed_1 >>>= 0;
    seed_2 >>>= 0;
    seed_3 >>>= 0;
    seed_4 >>>= 0;
    let cast32 = (seed_1 + seed_2) | 0;
    seed_1 = seed_2 ^ seed_2 >>> 9;
    seed_2 = seed_3 + (seed_3 << 3) | 0;
    seed_3 = (seed_3 << 21 | seed_3 >>> 11);
    seed_4 = seed_4 + 1 | 0;
    cast32 = cast32 + seed_4 | 0;
    seed_3 = seed_3 + cast32 | 0;
    return (cast32 >>> 0) / 4294967296;
  }
}

let generate_seed = MurmurHash3('String for the Seed Key');
let random_number = SimpleFastCounter32(generate_seed(), generate_seed());
console.log(random_number());
console.log(random_number());

Output:

0.837073584087193
0.3599331611767411

Use a Seed and Mulberry32 to Generate a Random Number

Mulberry32 is also a PRNG, though simpler in code structure than SFC32. Contrary to SFC32, which requires at least a seed.

We’ll use MurmurHash3 to generate the seed using a string. In the following example, we generate five random numbers using a for loop and Mulberry32.

Code:

// Define the Murmur3Hash function
function MurmurHash3(string) {
  let i = 0;
  for (i, hash = 1779033703 ^ string.length; i < string.length; i++) {
    let bitwise_xor_from_character = hash ^ string.charCodeAt(i);
    hash = Math.imul(bitwise_xor_from_character, 3432918353);
    hash = hash << 13 | hash >>> 19;
  }
  return () => {
    // Return the hash that you can use as a seed
    hash = Math.imul(hash ^ (hash >>> 16), 2246822507);
    hash = Math.imul(hash ^ (hash >>> 13), 3266489909);
    return (hash ^= hash >>> 16) >>> 0;
  }
}

function Mulberry32(string) {
  return () => {
    let for_bit32_mul = string += 0x6D2B79F5;
    let cast32_one = for_bit32_mul ^ for_bit32_mul >>> 15;
    let cast32_two = for_bit32_mul | 1;
    for_bit32_mul = Math.imul(cast32_one, cast32_two);
    for_bit32_mul ^= for_bit32_mul +
        Math.imul(for_bit32_mul ^ for_bit32_mul >>> 7, for_bit32_mul | 61);
    return ((for_bit32_mul ^ for_bit32_mul >>> 14) >>> 0) / 4294967296;
  }
}

let generate_seed = MurmurHash3('String for the Seed Key');
let random_number = Mulberry32(generate_seed());

for (let i = 0; i < 5; i++) {
  console.log(random_number());
}

Output:

0.13532060221768916
0.8630009586922824
0.53870237339288
0.5237146227154881
0.8748106376733631

Use a Seed and Xoshiro128** to Generate a Random Number

Professor Vagna and Blackman developed the Xoshiro128** generator. The xorshift128 is a family of the Xorshift PRNGs, and it’s the fastest PRNG.

Like SFC32, Xoshiro128** can take at least a seed before producing the random number. In the following snippet, we’ve created the required seed with MurmiurHash3.

Code:

// Define the Murmur3Hash function
function MurmurHash3(string) {
  let i = 0;
  for (i, hash = 1779033703 ^ string.length; i < string.length; i++) {
    let bitwise_xor_from_character = hash ^ string.charCodeAt(i);
    hash = Math.imul(bitwise_xor_from_character, 3432918353);
    hash = hash << 13 | hash >>> 19;
  }
  return () => {
    // Return the hash that you can use as a seed
    hash = Math.imul(hash ^ (hash >>> 16), 2246822507);
    hash = Math.imul(hash ^ (hash >>> 13), 3266489909);
    return (hash ^= hash >>> 16) >>> 0;
  }
}

function Xoshiro128_twostar(seed_1, seed_2, seed_3, seed_4) {
  return () => {
    let t = seed_2 << 9, y = seed_1 * 5;
    y = (y << 7 | y >>> 25) * 9;
    seed_3 ^= seed_1;
    seed_4 ^= seed_2;
    seed_2 ^= seed_3;
    seed_1 ^= seed_4;
    seed_3 ^= t;
    seed_4 = seed_4 << 11 | seed_4 >>> 21;
    return (y >>> 0) / 4294967296;
  }
}

let generate_seed = MurmurHash3('String for the Seed Key');
let random_number = Xoshiro128_twostar(generate_seed(), generate_seed());
console.log(random_number());

Output:

0.6150987280998379

Use a Seed and JSF to Generate a Random Number

Bob Jenkins created Jenkins Small Fast (JSF) generator, a fast generator. Though, it’s not fast when compared to SFC32.

When you observe the code of JSF, you’ll see similarities to SFC32. JSF can take more than a seed before producing a random number.

We generate ten random numbers with a seed and JSF in the next code.

Code:

// Define the Murmur3Hash function
function MurmurHash3(string) {
  let i = 0;
  for (i, hash = 1779033703 ^ string.length; i < string.length; i++) {
    let bitwise_xor_from_character = hash ^ string.charCodeAt(i);
    hash = Math.imul(bitwise_xor_from_character, 3432918353);
    hash = hash << 13 | hash >>> 19;
  }
  return () => {
    // Return the hash that you can use as a seed
    hash = Math.imul(hash ^ (hash >>> 16), 2246822507);
    hash = Math.imul(hash ^ (hash >>> 13), 3266489909);
    return (hash ^= hash >>> 16) >>> 0;
  }
}

function JenkinsSimpleFast32(seed_1, seed_2, seed_3, seed_4) {
  return () => {
    seed_1 |= 0;
    seed_2 |= 0;
    seed_3 |= 0;
    seed_4 |= 0;
    let t = seed_1 - (seed_2 << 27 | seed_2 >>> 5) | 0;
    seed_1 = seed_2 ^ (seed_3 << 17 | seed_3 >>> 15);
    seed_2 = seed_3 + seed_4 | 0;
    seed_3 = seed_4 + t | 0;
    seed_4 = seed_1 + t | 0;
    return (seed_4 >>> 0) / 4294967296;
  }
}

let generate_seed = MurmurHash3('String for the Seed Key');
let random_number = JenkinsSimpleFast32(generate_seed(), generate_seed());
for (let i = 0; i < 10; i++) {
  console.log(random_number());
}

Output:

0.513338076416403
0.4737987464759499
0.5743723993655294
0.4811882192734629
0.07753282226622105
0.11416710214689374
0.1270705321803689
0.15759771666489542
0.16906401910819113
0.6846413582097739

Use seedrandom.js to Generate a Random Number

seedrandom.js is a library by David Bau designed for seeded Random Number Generator (RNG), and it’s available on NPM and CDNJS. For this article, we’ll use CDNJS.

Keep the following in mind when using Seedrandom.js.

  1. You initialize seedrandom using new Math.seedrandom('seed key').
  2. You can use the quick() function of Seedrandom to generate 32-bit of randomness.
  3. The int32() function of Seedrandom.js returns a 32-bit signed integer.
  4. Calling seedrandom with no arguments causes the creation of an auto-seeded ARC4-based PRNG. The auto-seeding uses some values like the accumulated local entropy.
  5. Seedrandom can take an object as a second argument. This object is {entropy: true}, and the result is unpredictable.
  6. Calling Math.seedrandom without the new keyword replaces the default Math.random(). The replacement is new Math.seedrandom().

In this example, we’ve imported seedrandom.js from CDNJS. Afterward, we use it to generate a random number using a seed.

Code:

<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/seedrandom/3.0.5/seedrandom.min.js"></script>
    <script>
        let generate_random_number = new Math.seedrandom('Johnson');
        console.log(generate_random_number());
    </script>
</body>

Output:

0.08103389758898699

You can mix your seed with the accumulated entropy. You’ll need to pass {entropy: true} as the second argument of seedrandom.

Code:

<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/seedrandom/3.0.5/seedrandom.min.js"></script>
    <script>
        let generate_random_number = new Math.seedrandom('Antananarivo', { entropy: true });
        for (let i = 0; i < 5; i++) {
            console.log(generate_random_number());
        }
    </script>
</body>

Output:

0.8478730572111559
0.963664252064149
0.6002684820777331
0.4026776455839767
0.7579996916288508

In addition, quick() and int32() will return random 32-bit random numbers. The former will return a float, while the latter returns a signed integer.

Code:

<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/seedrandom/3.0.5/seedrandom.min.js"></script>
    <script>
        let generate_random_number = new Math.seedrandom('DavidBau');
        console.log("With quick():", generate_random_number.quick());
        console.log("With int32():", generate_random_number.int32());
    </script>
</body>

Output:

With quick(): 0.249648863915354
With int32(): -550219731
Habdul Hazeez avatar Habdul Hazeez avatar

Habdul Hazeez is a technical writer with amazing research skills. He can connect the dots, and make sense of data that are scattered across different media.

LinkedIn

Related Article - JavaScript Number