Pregunta

El JavaScript Math.random La función () devuelve un valor aleatorio entre 0 y 1, automáticamente sembrado según la hora actual (similar a Java, creo). Sin embargo, no creo que haya ninguna manera de establecer tu propia semilla para ello.

¿Cómo puedo crear un generador de números aleatorios para el que pueda proporcionar mi propio valor de semilla, de modo que pueda hacer que produzca una secuencia repetible de (pseudo) números aleatorios?

¿Fue útil?

Solución

Una opción es http://davidbau.com/seedrandom que es un Math.random basado en RC4 que se puede sembrar. () Reemplazo directo con buenas propiedades.

Otros consejos

Si no necesita la capacidad de inicialización, simplemente use Math.random () y cree las funciones de ayuda a su alrededor (por ejemplo, randRange (inicio, fin) ) .

No estoy seguro de qué RNG está utilizando, pero es mejor saberlo y documentarlo para que esté al tanto de sus características y limitaciones.

Como dijo Starkii, Mersenne Twister es un buen PRNG, pero no es fácil de implementar. Si desea hacerlo usted mismo, intente implementar un LCG : es muy fácil, tiene cualidades de aleatoriedad decentes. (no tan bueno como Mersenne Twister), y puedes usar algunas de las constantes populares.

function RNG(seed) {
  // LCG using GCC's constants
  this.m = 0x80000000; // 2**31;
  this.a = 1103515245;
  this.c = 12345;

  this.state = seed ? seed : Math.floor(Math.random() * (this.m - 1));
}
RNG.prototype.nextInt = function() {
  this.state = (this.a * this.state + this.c) % this.m;
  return this.state;
}
RNG.prototype.nextFloat = function() {
  // returns in range [0,1]
  return this.nextInt() / (this.m - 1);
}
RNG.prototype.nextRange = function(start, end) {
  // returns in range [start, end): including start, excluding end
  // can't modulu nextInt because of weak randomness in lower bits
  var rangeSize = end - start;
  var randomUnder1 = this.nextInt() / this.m;
  return start + Math.floor(randomUnder1 * rangeSize);
}
RNG.prototype.choice = function(array) {
  return array[this.nextRange(0, array.length)];
}

var rng = new RNG(20);
for (var i = 0; i < 10; i++)
  console.log(rng.nextRange(10, 50));

var digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
for (var i = 0; i < 10; i++)
  console.log(rng.choice(digits));

Si desea poder especificar la semilla, solo necesita reemplazar las llamadas a getSeconds () y getMinutes () . Podrías pasar un int y usar la mitad de ella mod 60 para el valor de segundos y la otra mitad modulo 60 para darte la otra parte.

Dicho esto, este método parece basura. Hacer la generación correcta de números aleatorios es muy difícil. El problema obvio con esto es que la semilla de números aleatorios se basa en segundos y minutos. Para adivinar la semilla y recrear su flujo de números aleatorios solo se requieren 3600 combinaciones diferentes de segundos y minutos. También significa que solo hay 3600 semillas posibles diferentes. Esto es corregible, pero sospecharía de este RNG desde el principio.

Si quieres usar un RNG mejor, prueba el Mersenne Twister . Es un RNG bien probado y bastante robusto con una órbita enorme y un rendimiento excelente.

EDITAR: Realmente debería estar en lo correcto y referirme a esto como un generador de números pseudoaleatorios o PRNG.

  

"Cualquier persona que use métodos aritméticos para producir números aleatorios se encuentra en estado de pecado".   & nbsp; & n & n & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & n & n & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & n & n & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & n & n & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & n & n & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & n & n & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; --- John von Neumann

Utilizo un puerto JavaScript del Mersenne Twister: https://gist.github.com/300494 Te permite configurar la semilla manualmente. Además, como se mencionó en otras respuestas, el Mersenne Twister es un PRNG realmente bueno.

El código que mencionaste es como Lehmer RNG . Si este es el caso, entonces 2147483647 es el mayor entero con signo de 32 bits, 2147483647 es el principal de 32 bits más grande, y 48271 es un multiplicador de período completo que se utiliza para generar los números.

Si esto es cierto, puede modificar RandomNumberGenerator para tomar un parámetro adicional semilla , y luego configurar this.seed a semilla ; pero deberías tener cuidado de asegurarte de que la semilla dé lugar a una buena distribución de números aleatorios (Lehmer puede ser así de raro), pero la mayoría de las semillas estarán bien.

El siguiente es un PRNG que puede ser alimentado con una semilla personalizada. Llamar a SeedRandom devolverá una función de generador aleatorio. Se puede llamar a SeedRandom sin argumentos para inicializar la función aleatoria devuelta con la hora actual, o se puede llamar con 1 o 2 inters no negativos como argumentos para poder inicializarlos con enteros Debido a la precisión de punto flotante, la siembra con solo 1 valor solo permitirá que el generador se inicie en uno de 2 ^ 53 estados diferentes.

La función generadora aleatoria devuelta toma 1 argumento entero llamado límite , el límite debe estar en el rango de 1 a 4294965886, la función devolverá un número en el rango de 0 a límite-1.

function SeedRandom(state1,state2){
    var mod1=4294967087
    var mul1=65539
    var mod2=4294965887
    var mul2=65537
    if(typeof state1!="number"){
        state1=+new Date()
    }
    if(typeof state2!="number"){
        state2=state1
    }
    state1=state1%(mod1-1)+1
    state2=state2%(mod2-1)+1
    function random(limit){
        state1=(state1*mul1)%mod1
        state2=(state2*mul2)%mod2
        if(state1<limit && state2<limit && state1<mod1%limit && state2<mod2%limit){
            return random(limit)
        }
        return (state1+state2)%limit
    }
    return random
}

Ejemplo de uso:

var generator1=SeedRandom() //Seed with current time
var randomVariable=generator1(7) //Generate one of the numbers [0,1,2,3,4,5,6]
var generator2=SeedRandom(42) //Seed with a specific seed
var fixedVariable=generator2(7) //First value of this generator will always be
                                //1 because of the specific seed.

Este generador exhibe las siguientes propiedades:

  • Tiene aproximadamente 2 ^ 64 diferentes estados internos posibles.
  • Tiene un período de aproximadamente 2 ^ 63, mucho más de lo que nadie necesitaría de manera realista en un programa de JavaScript.
  • Debido a que los valores de mod son primos, no hay un patrón simple en la salida, sin importar el límite elegido. Esto es diferente a algunos PRNG más simples que exhiben algunos patrones bastante sistemáticos.
  • Descarta algunos resultados para obtener una distribución perfecta sin importar el límite.
  • Es relativamente lento, corre alrededor de 10 000 000 veces por segundo en mi máquina.

Si programa en Typescript, adapté la implementación de Mersenne Twister que se presentó en la respuesta de Christoph Henkelmann a este hilo como una clase de script:

/**
 * copied almost directly from Mersenne Twister implementation found in https://gist.github.com/banksean/300494
 * all rights reserved to him.
 */
export class Random {
    static N = 624;
    static M = 397;
    static MATRIX_A = 0x9908b0df;
    /* constant vector a */
    static UPPER_MASK = 0x80000000;
    /* most significant w-r bits */
    static LOWER_MASK = 0x7fffffff;
    /* least significant r bits */

    mt = new Array(Random.N);
    /* the array for the state vector */
    mti = Random.N + 1;
    /* mti==N+1 means mt[N] is not initialized */

    constructor(seed:number = null) {
        if (seed == null) {
            seed = new Date().getTime();
        }

        this.init_genrand(seed);
    }

    private init_genrand(s:number) {
        this.mt[0] = s >>> 0;
        for (this.mti = 1; this.mti < Random.N; this.mti++) {
            var s = this.mt[this.mti - 1] ^ (this.mt[this.mti - 1] >>> 30);
            this.mt[this.mti] = (((((s & 0xffff0000) >>> 16) * 1812433253) << 16) + (s & 0x0000ffff) * 1812433253)
                + this.mti;
            /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
            /* In the previous versions, MSBs of the seed affect   */
            /* only MSBs of the array mt[].                        */
            /* 2002/01/09 modified by Makoto Matsumoto             */
            this.mt[this.mti] >>>= 0;
            /* for >32 bit machines */
        }
    }

    /**
     * generates a random number on [0,0xffffffff]-interval
     * @private
     */
    private _nextInt32():number {
        var y:number;
        var mag01 = new Array(0x0, Random.MATRIX_A);
        /* mag01[x] = x * MATRIX_A  for x=0,1 */

        if (this.mti >= Random.N) { /* generate N words at one time */
            var kk:number;

            if (this.mti == Random.N + 1)   /* if init_genrand() has not been called, */
                this.init_genrand(5489);
            /* a default initial seed is used */

            for (kk = 0; kk < Random.N - Random.M; kk++) {
                y = (this.mt[kk] & Random.UPPER_MASK) | (this.mt[kk + 1] & Random.LOWER_MASK);
                this.mt[kk] = this.mt[kk + Random.M] ^ (y >>> 1) ^ mag01[y & 0x1];
            }
            for (; kk < Random.N - 1; kk++) {
                y = (this.mt[kk] & Random.UPPER_MASK) | (this.mt[kk + 1] & Random.LOWER_MASK);
                this.mt[kk] = this.mt[kk + (Random.M - Random.N)] ^ (y >>> 1) ^ mag01[y & 0x1];
            }
            y = (this.mt[Random.N - 1] & Random.UPPER_MASK) | (this.mt[0] & Random.LOWER_MASK);
            this.mt[Random.N - 1] = this.mt[Random.M - 1] ^ (y >>> 1) ^ mag01[y & 0x1];

            this.mti = 0;
        }

        y = this.mt[this.mti++];

        /* Tempering */
        y ^= (y >>> 11);
        y ^= (y << 7) & 0x9d2c5680;
        y ^= (y << 15) & 0xefc60000;
        y ^= (y >>> 18);

        return y >>> 0;
    }

    /**
     * generates an int32 pseudo random number
     * @param range: an optional [from, to] range, if not specified the result will be in range [0,0xffffffff]
     * @return {number}
     */
    nextInt32(range:[number, number] = null):number {
        var result = this._nextInt32();
        if (range == null) {
            return result;
        }

        return (result % (range[1] - range[0])) + range[0];
    }

    /**
     * generates a random number on [0,0x7fffffff]-interval
     */
    nextInt31():number {
        return (this._nextInt32() >>> 1);
    }

    /**
     * generates a random number on [0,1]-real-interval
     */
    nextNumber():number {
        return this._nextInt32() * (1.0 / 4294967295.0);
    }

    /**
     * generates a random number on [0,1) with 53-bit resolution
     */
    nextNumber53():number {
        var a = this._nextInt32() >>> 5, b = this._nextInt32() >>> 6;
        return (a * 67108864.0 + b) * (1.0 / 9007199254740992.0);
    }
}

puedes usarlo de la siguiente manera:

var random = new Random(132);
random.nextInt32(); //return a pseudo random int32 number
random.nextInt32([10,20]); //return a pseudo random int in range [10,20]
random.nextNumber(); //return a a pseudo random number in range [0,1]

compruebe la fuente para más métodos.

  

Nota: Este código se incluyó originalmente en la pregunta anterior. Con el fin de mantener la pregunta breve y enfocada, la he movido a esta respuesta de la Comunidad Wiki.

Encontré este código dando vueltas y parece que funciona bien para obtener un número aleatorio y luego usar la semilla después, pero no estoy muy seguro de cómo funciona la lógica (por ejemplo, de dónde provinieron los números 2345678901, 48271 y 2147483647 ).

function nextRandomNumber(){
  var hi = this.seed / this.Q;
  var lo = this.seed % this.Q;
  var test = this.A * lo - this.R * hi;
  if(test > 0){
    this.seed = test;
  } else {
    this.seed = test + this.M;
  }
  return (this.seed * this.oneOverM);
}

function RandomNumberGenerator(){
  var d = new Date();
  this.seed = 2345678901 + (d.getSeconds() * 0xFFFFFF) + (d.getMinutes() * 0xFFFF);
  this.A = 48271;
  this.M = 2147483647;
  this.Q = this.M / this.A;
  this.R = this.M % this.A;
  this.oneOverM = 1.0 / this.M;
  this.next = nextRandomNumber;
  return this;
}

function createRandomNumber(Min, Max){
  var rand = new RandomNumberGenerator();
  return Math.round((Max-Min) * rand.next() + Min);
}

//Thus I can now do:
var letters = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'];
var numbers = ['1','2','3','4','5','6','7','8','9','10'];
var colors = ['red','orange','yellow','green','blue','indigo','violet'];
var first = letters[createRandomNumber(0, letters.length)];
var second = numbers[createRandomNumber(0, numbers.length)];
var third = colors[createRandomNumber(0, colors.length)];

alert("Today's show was brought to you by the letter: " + first + ", the number " + second + ", and the color " + third + "!");

/*
  If I could pass my own seed into the createRandomNumber(min, max, seed);
  function then I could reproduce a random output later if desired.
*/

Bien, aquí está la solución que resolví.

Primero creas un valor de semilla utilizando " newseed () " función. Luego, pasa el valor semilla a la " srandom () " función. Por último, el " srandom () " La función devuelve un valor pseudoaleatorio entre 0 y 1.

El bit crucial es que el valor semilla se almacena dentro de una matriz. Si fuera simplemente un entero o flotante, el valor se sobrescribiría cada vez que se llamara a la función, ya que los valores de enteros, flotadores, cadenas, etc., se almacenan directamente en la pila frente a solo los punteros, como en el caso de matrices y otros objetos Por lo tanto, es posible que el valor de la semilla permanezca persistente.

Finalmente, es posible definir el " srandom () " función tal que es un método de la " Matemáticas " Objeto, pero lo dejo a usted para averiguar. ;)

¡Buena suerte!

JavaScript:

// Global variables used for the seeded random functions, below.
var seedobja = 1103515245
var seedobjc = 12345
var seedobjm = 4294967295 //0x100000000

// Creates a new seed for seeded functions such as srandom().
function newseed(seednum)
{
    return [seednum]
}

// Works like Math.random(), except you provide your own seed as the first argument.
function srandom(seedobj)
{
    seedobj[0] = (seedobj[0] * seedobja + seedobjc) % seedobjm
    return seedobj[0] / (seedobjm - 1)
}

// Store some test values in variables.
var my_seed_value = newseed(230951)
var my_random_value_1 = srandom(my_seed_value)
var my_random_value_2 = srandom(my_seed_value)
var my_random_value_3 = srandom(my_seed_value)

// Print the values to console. Replace "WScript.Echo()" with "alert()" if inside a Web browser.
WScript.Echo(my_random_value_1)
WScript.Echo(my_random_value_2)
WScript.Echo(my_random_value_3)

Lua 4 (mi entorno de destino personal):

-- Global variables used for the seeded random functions, below.
seedobja = 1103515.245
seedobjc = 12345
seedobjm = 4294967.295 --0x100000000

-- Creates a new seed for seeded functions such as srandom().
function newseed(seednum)
    return {seednum}
end

-- Works like random(), except you provide your own seed as the first argument.
function srandom(seedobj)
    seedobj[1] = mod(seedobj[1] * seedobja + seedobjc, seedobjm)
    return seedobj[1] / (seedobjm - 1)
end

-- Store some test values in variables.
my_seed_value = newseed(230951)
my_random_value_1 = srandom(my_seed_value)
my_random_value_2 = srandom(my_seed_value)
my_random_value_3 = srandom(my_seed_value)

-- Print the values to console.
print(my_random_value_1)
print(my_random_value_2)
print(my_random_value_3)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top