Sembrando el generador de números aleatorios en Javascript


¿Es posible sembrar el generador de números aleatorios (Math.random) en Javascript?

Author: jww, 2009-02-06

12 answers

No, no lo es, pero es bastante fácil escribir su propio generador, o mejor aún usar uno existente. Echa un vistazo: esta pregunta relacionada.

También, ver el blog de David Bau para más información sobre la siembra.

 152
Author: PeterAllenWebb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:45

Mi otra respuesta representa un algoritmo más tradicional, pero encontré que el comentario de Dave Scotese a esta respuesta es más elocuente. Desafortunadamente, es bastante lento debido a la manipulación de cadenas.

Aquí hay una versión que es aproximadamente 20 veces más rápida y un poco más precisa también.

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

Puede establecer seed para ser cualquier número, simplemente evite cero (o cualquier múltiplo de Matemáticas.PI).

La elegancia de esta solución, en mi opinión, proviene de la falta de cualquier " magia" números (además de 10000, que representa aproximadamente la cantidad mínima de dígitos que debe tirar para evitar patrones impares-ver resultados con valores 10, 100, 1000). La brevedad también es agradable.

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

 116
Author: Antti Sykäri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:45

No, pero aquí hay un generador pseudoaleatorio simple que adapté de Wikipedia :

var m_w = 123456789;
var m_z = 987654321;
var mask = 0xffffffff;

// Takes any integer
function seed(i) {
    m_w = i;
    m_z = 987654321;
}

// Returns number between 0 (inclusive) and 1.0 (exclusive),
// just like Math.random().
function random()
{
    m_z = (36969 * (m_z & 65535) + (m_z >> 16)) & mask;
    m_w = (18000 * (m_w & 65535) + (m_w >> 16)) & mask;
    var result = ((m_z << 16) + m_w) & mask;
    result /= 4294967296;
    return result + 0.5;
}

EDITAR: se corrigió la función seed haciéndola resetear m_z

 33
Author: Antti Sykäri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-05 16:23:29

El algoritmo de Antti Sykäri es bonito y corto. Inicialmente hice una variación que reemplazó las matemáticas de Javascript.al azar cuando llamas a Matemáticas.seed (s), pero entonces Jason comentó que devolver la función sería mejor:

Math.seed = function(s) {
    return function() {
        s = Math.sin(s) * 10000; return s - Math.floor(s);
    };
};

// usage:
var random1 = Math.seed(42);
var random2 = Math.seed(random1());
Math.random = Math.seed(random2());

Esto le da otra funcionalidad que Javascript no tiene: múltiples generadores aleatorios independientes. Esto es especialmente importante si desea tener varias simulaciones repetibles ejecutándose al mismo tiempo.

 23
Author: Remco Kranenburg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-19 12:58:26

He implementado una serie de funciones PRNG buenas, cortas y rápidas para copiar y pegar. Todos ellos pueden ser sembrados.

Recuerde sembrar sus PRNGs correctamente. Muchos de los generadores a continuación no tienen siembra incorporada. Puede usar un algoritmo hash para generar una semilla a partir de un entero o cadena. Aquí hay un ejemplo basado en FNV1a-Mulvey hash:

function xfnv1a(k) {
    for(var i = 0, h = 2166136261 >>> 0; i < k.length; i++)
        h = Math.imul(h ^ k.charCodeAt(i), 16777619);
    return function() {
        h += h << 13; h ^= h >>> 7;
        h += h << 3;  h ^= h >>> 17;
        return (h += h << 5) >>> 0;
    }
}

xfnv1a transforma el FNV1a modificado de Bret Mulvey en lo que es esencialmente un PRNG. Como PRNG probablemente no sea muy bueno, pero para lo que es bueno es para generar un pool de entropía para otro PRNG. Dado que hashes una cadena, incluso un solo bit volteado producirá resultados muy diferentes. La mayoría de los PRNGs necesitan entropía suficiente en el estado inicial para proporcionar resultados decentes. Algunas implementaciones pre-ejecutan un PRNG hasta que produce resultados aceptables, mitigando de alguna manera la necesidad de un estado inicial bien distribuido.

Así es como lo usarías:

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

// Or: output one 32-bit hash to produce the seed for mulberry32.
var rand = mulberry32(seed());

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

En adelante a la mercancías.


Sfc32

Esta gema proviene de la suite de pruebas de números aleatorios de PractRand, de la cual pasa sin problemas. PractRand es supuestamente incluso más estricto que TestU01. También es muy rápido en JS (xoshiro128 * * es ligeramente más rápido, pero de peor calidad). Es probablemente mi PRNG de elección.

function sfc32(a, b, c, d) {
    return function() {
      a >>>= 0; b >>>= 0; c >>>= 0; d >>>= 0; 
      var t = (a + b) | 0;
      a = b ^ b >>> 9;
      b = c + (c << 3) | 0;
      c = (c << 21 | c >>> 11);
      d = d + 1 | 0;
      t = t + d | 0;
      c = c + t | 0;
      return (t >>> 0) / 4294967296;
    }
}

Mulberry32

Mulberry32 también es bastante rápido y tiene buena calidad (el autor afirma que pasa todas las pruebas de gjrand). Yo recomendaría esto si solo necesita un PRNG simple pero decente.

Tiene un estado de 32-bits y un período completo de 232. Ideal si solo desea sembrar con un entero de 32 bits y no le importa el problema de cumpleaños . Hay 4.3 mil millones de estados en comparación con los 340 undecillion en sfc32 / xoshiro128**.

function mulberry32(a) {
    return function() {
      var t = a += 0x6D2B79F5;
      t = Math.imul(t ^ t >>> 15, t | 1);
      t ^= t + Math.imul(t ^ t >>> 7, t | 61);
      return ((t ^ t >>> 14) >>> 0) / 4294967296;
    }
}

Alea

Alea es buena para aleatoriedad rápida y de alta calidad (diseñada específicamente para JS):

function Alea(seed) {
    if(seed === undefined) {seed = +new Date() + Math.random();}
    function Mash() {
        var n = 4022871197;
        return function(r) {
            for(var t, s, u = 0, e = 0.02519603282416938; u < r.length; u++)
            s = r.charCodeAt(u), f = (e * (n += s) - (n*e|0)),
            n = 4294967296 * ((t = f * (e*n|0)) - (t|0)) + (t|0);
            return (n|0) * 2.3283064365386963e-10;
        }
    }
    return function() {
        var m = Mash(), a = m(" "), b = m(" "), c = m(" "), x = 1, y;
        seed = seed.toString(), a -= m(seed), b -= m(seed), c -= m(seed);
        a < 0 && a++, b < 0 && b++, c < 0 && c++;
        return function() {
            var y = x * 2.3283064365386963e-10 + a * 2091639; a = b, b = c;
            return c = y - (x = y|0);
        };
    }();
}

Este tiene una siembra incorporada utilizando el Mash función. No incluye el método fract52 del original. Si eso es importante, es simplemente una transformación adicional de la salida.

Su velocidad es bastante buena y parece tener un estado de 96 bits (pero no estoy seguro). El autor (Baagøe, 2010) afirma "El período está cerca de 2^116, pasa BigCrush, y es el PRNG javascript más rápido que conozco que lo hace". No puedo verificar que realmente pase BigCrush, pero he visto resultados de Dieharder que parece prometedor. Está incluido en la biblioteca seetrandom.

Actualización: La velocidad de este PRNG es bastante lenta en comparación con mis otras implementaciones. Probablemente no ralentizará su juego/aplicación en la práctica, pero si el rendimiento absoluto es un problema, considere uno de los otros.

Para usarlo, llame a la función principal para iniciar el PRNG con una semilla, luego llame a su función devuelta para generar números posteriores:

var rand = Alea("123");
rand(); // 0.4801303152926266

Xoshiro128 * *

A partir de mayo 2018, xoshiro128** es el nuevo miembro de la familia xorshift. Ofrece un estado de 128 bits y es súper rápido.

function xoshiro128ss(a, b, c, d) {
    return function() {
        var t = b << 9, r = a * 5; r = (r << 7 | r >>> 25) * 9;
        c ^= a; d ^= b;
        b ^= c; a ^= d; c ^= t;
        d = d << 11 | d >>> 21;
        return (r >>> 0) / 4294967296;
    }
}

Este PRNG es el último de Blackman/Vigna que también hizo xorshift128+ y xoroshiro utilizado en Google Chrome. Es notable como uno de los pocos PRNGS modernos de 32 bits. xoroshiro64** también es prometedor, pero solo tiene un estado de 64 bits y ha sido reemplazado en gran medida por xoshiro.

Use así:

var rand = xoshiro128ss( initseed([1, 2, 3, 4]) );
rand(); // 0.7410467516165227

Los autores afirman que pasa bien las pruebas de aleatoriedad (aunque con salvedades). Otros investigadores han señalado que falla algunas pruebas en BigCrush (particularmente LinearComp y BinaryRank). Pero no debería importar en la práctica, especialmente si el valor de 32 bits se convierte en un flotador entre 0-1 como estos PRNGs son. Sin embargo, si confías en los bits bajos de una manera incómoda, puede causar un problema.

JSF

Esto es JSF o smallprng por Bob Jenkins (2007), el tipo que hizo ISAAC y SpookyHash .

function JSF(seed) {
    function jsf() {
        var e = s[0] - (s[1]<<27 | s[1]>>5);
         s[0] = s[1] ^ (s[2]<<17 | s[2]>>15),
         s[1] = s[2] + s[3],
         s[2] = s[3] + e, s[3] = s[0] + e;
        return (s[3] >>> 0) / 4294967295; // 2^32-1
    }
    seed >>>= 0;
    var s = [0xf1ea5eed, seed, seed, seed];
    for(var i=0;i<20;i++) jsf();
    return jsf;
}

Debería ser bastante rápido, pero ejecuta jsf() 20 veces inicialmente a seed, lo que cuesta un poco. Al parecer, también funciona bien en las pruebas de PractRand. Tiene 128 bits de estado, sin embargo solo 32 bits son seedable (posiblemente elegido para evitar la necesidad de una función hash). Se supone que la duración promedio del período es de 2^126, pero no se ha determinado formalmente.

Usado así:

var rand = JSF(123);
rand(); // 0.098275076597929

Lehmer LCG

Este solo está aquí para ilustre cuán malos son algunos de los RNGS en otras respuestas aquí, como los métodos Math.sin o Math.PI. Es extremadamente rápido pero solo tiene un estado de 31 bits y falla las pruebas estadísticas.

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

Sin embargo, es una línea única, lo que es bueno :). Este es el 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 y el período son solo de 31 bits. Este es el tipo de PRNG que otros están tratando de reemplazar.

Funcionará, pero no lo usaría a menos que realmente necesite rendimiento y no se preocupe por la calidad de aleatoriedad (¿qué es aleatorio de todos modos?) o el tamaño del estado / período de 31 bits. Genial para un game jam o una demo o algo así.

Use así:

var rand = LCG(123);
rand(); // 0.45899124443531036

Parece que hay otros LCGs que te dan los 32 bits completos. No estoy seguro de si las semillas tienen que ser extrañas, o si estas son mejores/peores que las de Park-Miller.

var LCG=s=>()=>((s=Math.imul(741103597,s))>>>0)/2**32;
var LCG=s=>()=>((s=Math.imul(1597334677,s))>>>0)/2**32;
 15
Author: bryc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-31 05:58:31

Por favor, vea el trabajo de Pierre L'Ecuyer que se remonta a finales de la década de 1980 y principios de la década de 1990. Crear un (pseudo) generador de números aleatorios por su cuenta, si no es un experto, es bastante peligroso, porque hay una alta probabilidad de que los resultados no sean estadísticamente aleatorios o tengan un período pequeño. Pierre (y otros) han reunido algunos buenos (pseudo) generadores de números aleatorios que son fáciles de implementar. Uso uno de sus LFSR generador.

Https://www.iro.umontreal.ca / ~lecuyer / myftp / papers / handstat.pdf

Phil Troy

 10
Author: user2383235,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-04 18:56:59

Combinando algunas de las respuestas anteriores, esta es la función aleatoria seedable que está buscando:

Math.seed = function(s) {
    var m_w  = s;
    var m_z  = 987654321;
    var mask = 0xffffffff;

    return function() {
      m_z = (36969 * (m_z & 65535) + (m_z >> 16)) & mask;
      m_w = (18000 * (m_w & 65535) + (m_w >> 16)) & mask;

      var result = ((m_z << 16) + m_w) & mask;
      result /= 4294967296;

      return result + 0.5;
    }
}

var myRandomFunction = Math.seed(1234);
var randomNumber = myRandomFunction();

Tenga cuidado con este sin embargo, no creo que la distribución de números aleatorios es muy buena, parece que pesa hacia el 0 a .5 rango. Al menos esa fue mi experiencia en la visualización de caminata aleatoria que estaba haciendo.

 4
Author: user3158327,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-04 19:32:14

Escribir su propio generador pseudo aleatorio es bastante simple.

La sugerencia de Dave Scotese es útil pero, como han señalado otros, no está distribuida de manera muy uniforme.

Sin embargo, no es debido a los argumentos enteros del pecado. Es simplemente por el rango del pecado, que resulta ser una proyección unidimensional de un círculo. Si tomaras el ángulo del círculo, sería uniforme.

Así que en lugar de sin(x) use arg(exp(i * x)) / (2 * PI).

Si no te gusta el orden lineal, mézclalo un poco con xor. El factor real tampoco importa mucho.

Para generar n números pseudo aleatorios uno podría usar el código:

function psora(k, n) {
  var r = Math.PI * (k ^ n)
  return r - Math.floor(r)
}
n = 42; for(k = 0; k < n; k++) console.log(psora(k, n))

Tenga en cuenta también que no puede usar secuencias pseudo aleatorias cuando se necesita entropía real.

 3
Author: Lajos Bodrogi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-26 00:33:09

Muchas personas que necesitan un generador de números aleatorios en Javascript actualmente están usando El módulo seedrandom de David Bau.

 2
Author: Martin Omander,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-07 15:52:34

He escrito una función que devuelve un número aleatorio sembrado, utiliza Matemáticas.pecar para tener un número aleatorio largo y utiliza la semilla para escoger números de eso.

Uso:

SeedRandom ("k9]:2@", 15)

Devolverá su número sembrado el primer parámetro es cualquier valor de cadena; su semilla. el segundo parámetro es cuántos dígitos devolverán.

     function seedRandom(inputSeed, lengthOfNumber){

           var output = "";
           var seed = inputSeed.toString();
           var newSeed = 0;
           var characterArray = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','y','x','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','U','R','S','T','U','V','W','X','Y','Z','!','@','#','$','%','^','&','*','(',')',' ','[','{',']','}','|',';',':',"'",',','<','.','>','/','?','`','~','-','_','=','+'];
           var longNum = "";
           var counter = 0;
           var accumulator = 0;

           for(var i = 0; i < seed.length; i++){
                var a = seed.length - (i+1);
                for(var x = 0; x < characterArray.length; x++){
                     var tempX = x.toString();
                     var lastDigit = tempX.charAt(tempX.length-1);
                     var xOutput = parseInt(lastDigit);
                     addToSeed(characterArray[x], xOutput, a, i); 
                }                  
           }

                function addToSeed(character, value, a, i){
                     if(seed.charAt(i) === character){newSeed = newSeed + value * Math.pow(10, a)}
                }
                newSeed = newSeed.toString();

                var copy = newSeed;
           for(var i=0; i<lengthOfNumber*9; i++){
                newSeed = newSeed + copy;
                var x = Math.sin(20982+(i)) * 10000;
                var y = Math.floor((x - Math.floor(x))*10);
                longNum = longNum + y.toString()
           }

           for(var i=0; i<lengthOfNumber; i++){
                output = output + longNum.charAt(accumulator);
                counter++;
                accumulator = accumulator + parseInt(newSeed.charAt(counter));
           }
           return(output)
      }
 -1
Author: Tyler Hudson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-13 15:45:44

Un enfoque simple para una semilla fija:

function fixedrandom(p){
    const seed = 43758.5453123;
    return (Math.abs(Math.sin(p)) * seed)%1;
}
 -3
Author: Carlos Oliveira,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-30 02:24:18

Para un número entre 0 y 100.

Number.parseInt(Math.floor(Math.random() * 100))
 -5
Author: Lord Elrond,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-10 15:47:34