Saltar al contenido

Sembrando el generador de números aleatorios en Javascript

Basta ya de indagar por todo internet porque llegaste al espacio exacto, poseemos la solución que necesitas hallar sin problemas.

Solución:

He implementado una serie de buenos, breves y rápidos Generador de números pseudoaleatorios (PRNG) funciona en JavaScript simple. Todos ellos pueden ser sembrados y proporcionar números de buena calidad.

En primer lugar, asegúrese de inicializar correctamente sus PRNG. La mayoría de los generadores siguientes no tienen un procedimiento de generación de semillas incorporado (en aras de la simplicidad), pero aceptan uno o más valores de 32 bits como valores iniciales. estado del PRNG. Semillas similares (por ejemplo, una semilla simple de 1 y 2) pueden causar correlaciones en PRNG más débiles, lo que da como resultado que la salida tenga propiedades similares (como niveles generados aleatoriamente que sean similares). Para evitar esto, es una buena práctica inicializar los PRNG con una semilla bien distribuida.

Afortunadamente, las funciones hash son muy buenas para generar semillas para PRNG a partir de cadenas cortas. Una buena función hash generará resultados muy diferentes incluso cuando dos cadenas sean similares. Aquí hay un ejemplo basado en la función de mezcla de MurmurHash3:

function xmur3(str) 
    for(var i = 0, h = 1779033703 ^ str.length; i < str.length; i++)
        h = Math.imul(h ^ str.charCodeAt(i), 3432918353),
        h = h << 13 

Cada llamada posterior al función de retorno de xmur3 produce un nuevo valor hash "aleatorio" de 32 bits que se utilizará como semilla en un PRNG. Así es como puede usarlo:

// Create xmur3 state:
var seed = xmur3("apples");
// Output four 32-bit hashes to provide the seed for sfc32.
var rand = sfc32(seed(), seed(), seed(), seed());

// Output one 32-bit hash to provide the seed for mulberry32.
var rand = mulberry32(seed());

// Obtain sequential random numbers like so:
rand();
rand();

Alternativamente, simplemente elija algunos datos ficticios para rellenar la semilla y haga avanzar el generador unas cuantas veces (12-20 iteraciones) para mezclar el estado inicial completamente. Esto se ve a menudo en implementaciones de referencia de PRNG, pero limita el número de estados iniciales.

var seed = 1337 ^ 0xDEADBEEF; // 32-bit seed with optional XOR value
// Pad seed with Phi, Pi and E.
// https://en.wikipedia.org/wiki/Nothing-up-my-sleeve_number
var rand = sfc32(0x9E3779B9, 0x243F6A88, 0xB7E15162, seed);
for (var i = 0; i < 15; i++) rand();

La salida de estas funciones PRNG produce un número positivo de 32 bits (0 a 232-1) que luego se convierte en un número de punto flotante entre 0-1 (0 inclusive, 1 exclusivo) equivalente a Math.random(), si desea números aleatorios de un rango específico, lea este artículo sobre MDN. Si solo desea los bits sin procesar, simplemente elimine la operación de división final.

Otra cosa a tener en cuenta son las limitaciones de JS. Los números solo pueden representar enteros enteros hasta una resolución de 53 bits. Y cuando se usan operaciones bit a bit, esto se reduce a 32. Esto dificulta la implementación de algoritmos escritos en C o C ++, que usan números de 64 bits. La portabilidad de código de 64 bits requiere suplementos que pueden drásticamente reducir el rendimiento. Entonces, en aras de la simplicidad y la eficiencia, solo he considerado algoritmos que usan matemáticas de 32 bits, ya que son directamente compatibles con JS.

Ahora, adelante con los generadores. (Mantengo la lista completa con referencias aquí)


sfc32 (contador rápido simple)

sfc32 es parte del conjunto de pruebas de números aleatorios PractRand (que, por supuesto, pasa). sfc32 tiene un estado de 128 bits y es muy rápido en JS.

function sfc32(a, b, c, d) 
    return function()  c >>> 11);
      d = d + 1 

Mulberry32

Mulberry32 es un generador simple con un estado de 32 bits, pero es extremadamente rápido y tiene buena calidad (el autor afirma que pasa todas las pruebas de la suite de pruebas gjrand y tiene 232 punto, pero no lo he verificado).

function mulberry32(a) 
    return function() 
      var t = a += 0x6D2B79F5;
      t = Math.imul(t ^ t >>> 15, t 

Recomendaría esto si solo necesita un simple pero bueno PRNG y no necesita miles de millones de números aleatorios (consulte Problema de cumpleaños).

xoshiro128 **

En mayo de 2018, xoshiro128 ** es el nuevo miembro de la familia Xorshift, de Vigna & Blackman (el profesor Vigna también fue responsable del algoritmo Xorshift128 + que impulsa la mayoría Math.random implementaciones bajo el capó). Es el generador más rápido que ofrece un estado de 128 bits.

function xoshiro128ss(a, b, c, d) 
    return function()  d >>> 21;
        return (r >>> 0) / 4294967296;
    

Los autores afirman que pasa bien las pruebas de aleatoriedad (aunque con salvedades). Otros investigadores han señalado que fallan algunas pruebas en TestU01 (particularmente LinearComp y BinaryRank). En la práctica, no debería causar problemas cuando se utilizan flotantes (como estas implementaciones), pero puede causar problemas si se basa en los bits bajos sin procesar.

JSF (Pequeño ayuno de Jenkins)

Esto es JSF o 'smallprng' de Bob Jenkins (2007), el tipo que hizo ISAAC y SpookyHash. Pasa las pruebas de PractRand y debería ser bastante rápido, aunque no tan rápido como SFC.

function jsf32(a, b, c, d) 
    return function()  0;
        a = b ^ (c << 17 

LCG (también conocido como Lehmer / Park-Miller RNG o MCG)

LCG es extremadamente rápido y simple, pero la calidad de su aleatoriedad es tan baja que el uso inadecuado puede causar errores en su programa. No obstante, es significativamente mejor que algunas respuestas que sugieren usar Math.sin o Math.PI! Sin embargo, es de una sola línea, lo cual es bueno :).

var LCG=s=>()=>(2**31-1&(s=Math.imul(48271,s)))/2**31;

Esta implementación se llama estándar mínimo RNG propuesto por Park-Miller en 1988 y 1993 e implementado en C ++ 11 como minstd_rand. Tenga en cuenta que el estado es de 31 bits (31 bits dan 2 mil millones de estados posibles, 32 bits dan el doble). ¡Este es el tipo de PRNG que otros están tratando de reemplazar!

Funcionará, pero no lo usaría a menos que tú De Verdad necesitan velocidad y no les importa la calidad de la aleatoriedad (¿qué es aleatorio de todos modos?). Genial para un jam de juego o una demostración o algo así. Los LCG sufren de correlaciones de semillas, por lo que es mejor descartar el primer resultado de una LCG. Y si insiste en usar un LCG, agregar un valor de incremento puede mejorar los resultados, pero probablemente sea un ejercicio inútil cuando existen opciones mucho mejores.

Parece que hay otros multiplicadores que ofrecen un estado de 32 bits (espacio de estado aumentado):

var LCG=s=>()=>(s=Math.imul(741103597,s)>>>0)/2**32;
var LCG=s=>()=>(s=Math.imul(1597334677,s)>>>0)/2**32;

Estos valores de LCG son de: P. L'Ecuyer: Una tabla de generadores congruenciales lineales de diferentes tamaños y buena estructura de celosía, 30 de abril de 1997.

No, no es posible sembrar Math.random(), pero es bastante fácil escribir su propio generador, o mejor aún, usar uno existente.

Mira: esta pregunta relacionada.

Además, consulte el blog de David Bau para obtener más información sobre la siembra.

NOTA: A pesar de (o más bien, debido a) la concisión y la aparente elegancia, este algoritmo no es de ninguna manera uno de alta calidad en términos de aleatoriedad. Busque, por ejemplo, los que se enumeran en esta respuesta para obtener mejores resultados.

(Originalmente adaptado de una idea inteligente presentada en un comentario a otra respuesta).

var seed = 1;
function random() 
    var x = Math.sin(seed++) * 10000;
    return x - Math.floor(x);

Puedes configurar seed para ser cualquier número, simplemente evite cero (o cualquier múltiplo de Math.PI).

La elegancia de esta solución, en mi opinión, proviene de la falta de números "mágicos" (además de 10000, que representa aproximadamente la cantidad mínima de dígitos que debe desechar para evitar patrones extraños; vea los resultados con valores de 10, 100, 1000). ). La brevedad también es buena.

Es un poco más lento que Math.random () (por un factor de 2 o 3), pero creo que es tan rápido como cualquier otra solución escrita en JavaScript.

Puedes corroborar nuestra faena escribiendo un comentario y puntuándolo te lo agradecemos.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *