valores - Sembrando el generador de números aleatorios en Javascript
script numero aleatorio (12)
Combinando algunas de las respuestas anteriores, esta es la función aleatoria seleccionable 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();
Sin embargo, tenga cuidado al usar este, no creo que la distribución de números aleatorios sea muy buena, parece que pesa hacia el rango de 0 a .5. Al menos esa fue mi experiencia en la visualización de caminata aleatoria que estaba haciendo.
¿Es posible sembrar el generador de números aleatorios (Math.random) en Javascript?
Consulte el trabajo de Pierre L''Ecuyer que se remonta a finales de los 80 y principios de los 90. Hay otros también. Crear un (pseudo) generador de números aleatorios por su cuenta, si no es un experto, es bastante peligroso, ya que existe 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 generadores de números aleatorios buenos (seudo) que son fáciles de implementar. Yo uso uno de sus generadores LFSR.
https://www.iro.umontreal.ca/~lecuyer/myftp/papers/handstat.pdf
Phil Troy
El algoritmo de Antti Sykäri es agradable y corto. Inicialmente hice una variación que reemplazó a Math.random de Javascript cuando llamaste Math.seed (s), pero luego 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.
Escribir tu 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 bastante uniforme.
Sin embargo, no es debido a los argumentos enteros del pecado. Es simplemente debido al rango del pecado, que resulta ser una proyección unidimensional de un círculo. Si tomas el ángulo del círculo, sería uniforme.
Así que en lugar de sin (x) use arg (exp (i * x)) / (2 * PI).
Si no le gusta el orden lineal, mezcle un poco con xor. El factor real tampoco importa mucho.
Para generar n números pseudoaleatorios se 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 pseudoaleatorias cuando se necesita una entropía real.
He escrito una función que devuelve un número aleatorio sembrado, usa Math.sin para tener un número aleatorio largo y usa la semilla para escoger números de ese.
Utilizar :
seedRandom ("k9]: 2 @", 15)
devolverá su número sembrado el primer parámetro es cualquier valor de cadena; tu semilla El segundo parámetro es cuántos dígitos regresará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)
}
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 una cadena. Aquí hay un ejemplo basado en el hash FNV1a-Mulvey:
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 lo que es bueno es generar un grupo de entropía para otro PRNG. Dado que tiene una cadena, incluso un solo bit volteado producirá resultados muy diferentes. La mayoría de los PRNG necesitan suficiente entropía en el estado inicial para proporcionar resultados decentes. Algunas implementaciones ejecutan un PRNG hasta que produce resultados aceptables, lo que mitiga algo 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();
Hacia adelante a las mercancías.
sfc32
Esta gema proviene del conjunto de pruebas de números aleatorios PractRand, del 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). Probablemente es 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). Recomendaría esto si solo necesita un PRNG simple pero decente .
Tiene un estado de 32 bits y un período completo de 2 32 . Es ideal si solo quieres sembrar con un entero de 32 bits y no te importa el problema del cumpleaños . Hay 4,3 mil millones de estados en comparación con los 340 millones de millones 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 bueno para la 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 incorporada la siembra utilizando la función Mash
. No incluye el método fract52
del original. Si eso es importante, es simplemente una transformación adicional de la salida .
La velocidad es bastante buena y parece tener un estado de 96 bits (pero no estoy seguro). El autor (Baagøe, 2010) afirma que "el período se acerca a 2 ^ 116, pasa BigCrush y es el PRNG de javascript más rápido que conozco". No puedo verificar que efectivamente pase a BigCrush, pero he visto resultados de Dieharder que parecen prometedores. Se incluye en la biblioteca seedrandom .
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 subsiguientes:
var rand = Alea("123");
rand(); // 0.4801303152926266
xoshiro128 **
A partir de mayo de 2018, xoshiro128**
es el nuevo miembro de la familia xorshift. Ofrece un estado de 128 bits, y es super 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 PRNG 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.
Utilizar como tal:
var rand = xoshiro128ss( initseed([1, 2, 3, 4]) );
rand(); // 0.7410467516165227
Los autores afirman que pasa bien las pruebas de aleatoriedad ( aunque con advertencias ). 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 a una flotación entre 0-1 como lo son estos PRNG. Sin embargo, si confía 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 para sembrar, lo que cuesta un poco. Al parecer, también se desempeña bien en las pruebas de PractRand. Tiene 128 bits de estado, sin embargo, solo 32 bits se pueden sembrar (posiblemente se elijan para evitar requerir una función hash). Se supone que la duración del período promedio es 2 ^ 126 pero no se ha determinado formalmente. El estado completo de 128 bits se puede sembrar manualmente sin problema, pero el uso de la construcción original tiene algunas garantías con respecto a la duración del ciclo.
Utilizado como tal:
var rand = JSF(123);
rand(); // 0.098275076597929
Lehmer LCG
Este solo está aquí para ilustrar cuán malos son algunos de los RNG 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;
Es una frase de una sola línea que es agradable :). Este es el RNG estándar mínimo 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 intentan reemplazar.
Funcionará, pero no lo usaría a menos que realmente necesite rendimiento y no le importe la calidad de aleatoriedad (¿qué es aleatorio de todos modos?) O el estado / período de 31 bits. Ideal para un juego de mermelada o una demostración o algo así.
Utilizar como tal:
var rand = LCG(123);
rand(); // 0.45899124443531036
Parece que hay otros LCG que te dan los 32 bits completos. No estoy seguro 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;
Mucha gente que necesita un generador de números aleatorios en Javascript en estos días está usando seedrandom .
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 figuran 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);
}
Puede establecer que la seed
sea cualquier número, solo 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, lo que representa aproximadamente la cantidad mínima de dígitos que debe tirar para evitar patrones impares; vea los resultados con valores 10 , 100 , 1000 ). La brevedad también es agradable.
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.
No, no lo es, pero es bastante fácil escribir su propio generador, o mejor aún, usar uno existente. Echa un vistazo a: esta pregunta relacionada .
Además, consulte el blog de David Bau para obtener más información sobre la siembra .
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: función de semilla fija al hacer que se reinicie m_z
Para un número entre 0 y 100.
Number.parseInt(Math.floor(Math.random() * 100))
Un enfoque simple para una semilla fija:
function fixedrandom(p){
const seed = 43758.5453123;
return (Math.abs(Math.sin(p)) * seed)%1;
}