javascript - varios - valores aleatorios en js




Generador de números aleatorios JavaScript legible (7)

Nota: Este código se incluyó originalmente en la pregunta anterior. En aras de mantener la pregunta breve y enfocada, la he trasladado a esta respuesta Wiki comunitario.

Encontré este código dando vueltas y parece funcionar 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 vienen 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.
*/

La función JavaScript Math.random() 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 forma de establecer tu propia semilla para ello.

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


El código que enumeró se parece a un Lehmer RNG . Si este es el caso, entonces 2147483647 es el entero más grande con signo de 32 bits, 2147483647 es el primo más grande de 32 bits, 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 RandomNumberGenerator un valor adicional de parámetro y luego establecer this.seed para this.seed ; pero tendrías que tener cuidado para asegurarte de que la semilla daría como resultado una buena distribución de números aleatorios (Lehmer puede ser raro así), pero la mayoría de las semillas estarán bien.


OK, aquí está la solución que establecí.

Primero crea un valor de inicialización usando la función "nueva semilla ()". Luego pasas el valor inicial a la función "srandom ()". Por último, la función "srandom ()" devuelve un valor pseudoaleatorio entre 0 y 1.

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

Finalmente, es posible definir la función "srandom ()" de modo que sea un método del objeto "Math", pero lo dejo para que lo descubra. ;)

¡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 objetivo 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)

Si desea poder especificar la semilla, solo necesita reemplazar las llamadas a getSeconds() y getMinutes() . Podría pasar un int y usar la mitad de él mod 60 para el valor de segundos y la otra mitad módulo 60 para darle 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 del número aleatorio se basa en segundos y minutos. Adivinar la semilla y recrear su secuencia de números aleatorios solo requiere probar 3600 combinaciones diferentes de segundos y minutos. También significa que solo hay 3600 semillas diferentes posibles. Esto es corregible, pero desconfiarí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 gran órbita y un excelente rendimiento.

EDITAR: realmente debería ser correcto y referirme a esto como un generador de números aleatorios pseudo o PRNG.

"Cualquiera que use métodos aritméticos para producir números aleatorios está en un estado de pecado".
--- John von Neumann


Simplemente parametriza el constructor y establece la semilla:

function RandomNumberGenerator(Seed){
  var d = new Date();
  this.seed = Seed;
  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;
}

Y ajuste su función que crea el generador de números aleatorios de esta manera:

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

Y llame así:

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 seed = <generate seed>;
var first = createRandomNumber(seed, 0, letters.length);
var second = createRandomNumber(seed, 0, numbers.length);
var third = createRandomNumber(seed, 0, colors.length);


si no necesita la capacidad de siembra, simplemente use Math.random() y construya funciones auxiliares a su alrededor (por ejemplo, randRange(start, end) ).

No estoy seguro de qué RNG está usando, pero lo mejor es conocerlo y documentarlo para que sepa cuáles son sus características y limitaciones.

Como dijo Starkii, Mersenne Twister es un buen PRNG, pero no es fácil de implementar. Si quieres hacerlo tú mismo, intenta implementar un LCG : es muy fácil, tiene buenas cualidades de aleatoriedad (no tan buenas 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));





seed