Use una semilla para generar números aleatorios en JavaScript

Habdul Hazeez 12 octubre 2023
  1. Use una semilla y SFC32 para generar un número aleatorio
  2. Use una semilla y Mulberry32 para generar un número aleatorio
  3. Use una semilla y Xoshiro128** para generar un número aleatorio
  4. Use una semilla y JSF para generar un número aleatorio
  5. Use seedrandom.js para generar un número aleatorio
Use una semilla para generar números aleatorios en JavaScript

Este artículo aborda cómo usar una semilla para generar números aleatorios a partir de PRNG. Mientras tanto, es una buena práctica asegurarse de que la semilla de los PRNG tenga una entropía alta.

Por lo tanto, usaremos una función hash para generar la semilla. Posteriormente, pasamos la semilla al PRNG.

Use una semilla y SFC32 para generar un número aleatorio

SFC32 o Simple Fast Counter es un PRNG rápido de PractRand (principalmente en C), y tiene una implementación en JavaScript con un estado de 128 bits, y es muy rápido. SFC32 requiere al menos una sola semilla para generar el número aleatorio.

Generaremos esta semilla usando la función hash, una implementación de JavaScript de MurmurHash3, que requiere una cadena inicial para generar la semilla. Como resultado, pasamos una cadena.

En el siguiente código, generamos la semilla y la pasamos a SFC32 que devuelve el número aleatorio.

Código:

// 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());

Producción :

0.837073584087193
0.3599331611767411

Use una semilla y Mulberry32 para generar un número aleatorio

Mulberry32 también es un PRNG, aunque tiene una estructura de código más simple que SFC32. Al contrario de SFC32, que requiere al menos una semilla.

Usaremos MurmurHash3 para generar la semilla usando una cadena. En el siguiente ejemplo, generamos cinco números aleatorios usando un bucle for y Mulberry32.

Código:

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

Producción :

0.13532060221768916
0.8630009586922824
0.53870237339288
0.5237146227154881
0.8748106376733631

Use una semilla y Xoshiro128** para generar un número aleatorio

El profesor Vagna y Blackman desarrollaron el generador Xoshiro128**. El xorshift128 es una familia de los PRNG Xorshift, y es el PRNG más rápido.

Al igual que SFC32, Xoshiro128** puede tomar al menos una semilla antes de producir el número aleatorio. En el siguiente fragmento, hemos creado la semilla necesaria con MurmiurHash3.

Código:

// 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());

Producción :

0.6150987280998379

Use una semilla y JSF para generar un número aleatorio

Bob Jenkins creó el generador Jenkins Small Fast (JSF), un generador rápido. Sin embargo, no es rápido en comparación con SFC32.

Cuando observe el código de JSF, verá similitudes con SFC32. JSF puede tomar más de una semilla antes de producir un número aleatorio.

Generamos diez números aleatorios con una semilla y JSF en el siguiente código.

Código:

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

Producción :

0.513338076416403
0.4737987464759499
0.5743723993655294
0.4811882192734629
0.07753282226622105
0.11416710214689374
0.1270705321803689
0.15759771666489542
0.16906401910819113
0.6846413582097739

Use seedrandom.js para generar un número aleatorio

seedrandom.js es una biblioteca de David Bau diseñada para generador de números aleatorios (RNG) con semillas, y está disponible en NPM y CDNJS. Para este artículo, usaremos CDNJS.

Tenga en cuenta lo siguiente cuando utilice Seedrandom.js.

  1. Inicializas seedrandom usando new Math.seedrandom('seed key').
  2. Puede utilizar la función quick() de Seedrandom para generar 32 bits de aleatoriedad.
  3. La función int32() de Seedradom.js devuelve un entero de 32 bits con signo.
  4. Llamar a seedrandom sin argumentos provoca la creación de un PRNG basado en ARC4 autosembrado. La siembra automática utiliza algunos valores como la entropía local acumulada.
  5. Seedrandom puede tomar un objeto como segundo argumento. Este objeto es {entropy: true}, y el resultado es impredecible.
  6. Llamar a Math.seedrandom sin la palabra clave new reemplaza el valor predeterminado Math.random(). El reemplazo es new Math.seedrandom().

En este ejemplo, hemos importado seedrandom.js de CDNJS. Luego, lo usamos para generar un número aleatorio usando una semilla.

Código:

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

Producción :

0.08103389758898699

Puedes mezclar tu semilla con la entropía acumulada. Deberá pasar {entropy: true} como segundo argumento de seedrandom.

Código:

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

Producción :

0.8478730572111559
0.963664252064149
0.6002684820777331
0.4026776455839767
0.7579996916288508

Además, quick() e int32() devolverán números aleatorios aleatorios de 32 bits. El primero devolverá un flotante, mientras que el último devolverá un entero con signo.

Código:

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

Producción :

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

Artículo relacionado - JavaScript Number