Abre tus herramientas de desarrollo (Mac: cmd + opción + i / Windows: ctrl + shift + i), ve a la consola, escribe Math.random()
y presiona regresar.
Bam. Obtienes un número aleatorio.
Obtuve 0.6199322557631561.
Siempre me he preguntado de dónde diablos vienen estos números. Y, lo que es más importante, ¿cómo es posible que sean aleatorios? Después de todo, ¿acaso las computadoras no toman algo de entrada, lo hacen girar con algunas matemáticas y luego lo escupen? Parece un proceso bastante predecible. Entonces, ¿qué sucede cuando quieres generar un número 'aleatorio'? ¿Cómo funciona eso y qué sucede detrás de escena?
Sorpresa sorpresa, la respuesta es que Math.random()
realmente no genera un número aleatorio. No exactamente. Simplemente hace un muy buen trabajo al simular la aleatoriedad.
La generación algorítmica de números aleatorios no puede ser exactamente aleatoria per se; Es por eso que se les llama más acertadamente generadores de números pseudoaleatorios (PRNG). Si está utilizando matemáticas y fórmulas para crear una secuencia de números, aunque parezcan aleatorios, esos números eventualmente se repetirán y revelarán un patrón no aleatorio.
Pero algunos PRNG son mejores que otros. La calidad de un PRNG depende de una serie de factores, siendo un factor muy importante algo llamado su período ; el número de iteraciones por las que pasa un PRNG antes de que empiece a repetirse. Un PRNG con un período largo no solo nos parece más aleatorio a los humanos, sino que también es más difícil (es decir, más intensivo en recursos) para que una computadora lo descifre/prediga; un hecho que tiene implicaciones de seguridad a pesar de que nadie debería usar Math.random()
para el cifrado, pero sucede de todos modos.
Entonces ahora la pregunta es: ¿qué PRNG usa JavaScript ?
La respuesta: ninguno.
JavaScript no decide cómo se implementa Math.random()
, lo hace su navegador. No hay un algoritmo PRNG codificado que se envíe con JavaScript. En su lugar, los ingenieros que crearon su navegador decidieron usar un algoritmo que se ajusta a la especificación ECMAScript, que es el siguiente:
[Math.random] Devuelve un valor numérico con signo positivo, mayor o igual a 0 pero menor a 1, elegido aleatoriamente o pseudoaleatoriamente con una distribución aproximadamente uniforme en ese rango, usando una estrategia o algoritmo dependiente de la implementación. Esta función no acepta argumentos.
Cada función Math.random creada para código distinto Realms debe producir una secuencia distinta de valores a partir de llamadas sucesivas.
Esas son las instrucciones, depende del navegador decidir cómo seguirlas. Hasta hace poco, diferentes navegadores usaban métodos ligeramente diferentes para lograr esto. Los algoritmos que usaron tenían nombres atractivos como Marsenne-Twister , Multiply With Carry o Linear Congruential Generator. Sin embargo, no te preocupes, no es realmente importante que entiendas lo que significan todas esas cosas (aunque estoy completamente impresionado si lo haces).
Lo importante que debe saber sobre todo esto es que (1) los navegadores deciden qué algoritmo quieren usar para calcular Math.random()
y (2) en 2015 casi todos los navegadores (al menos los principales) abandonaron sus antiguos algoritmos PRNG y ahora todos usan el mismo: llamado xorshift128+ .
Resulta que xorshift128+ hace un trabajo significativamente mejor al ser aleatorio que los algoritmos más antiguos; además, es extremadamente liviano y computacionalmente rápido. Por lo tanto, se adoptó prácticamente en todos los ámbitos, lo que dice mucho de su eficacia si se considera que anteriormente había muchas opiniones diferentes sobre el asunto.
Pero, ¿cómo funciona exactamente?
Si bien existen ligeras variaciones en la forma en que cada navegador implementa el algoritmo, podemos ver una especie de versión "vainilla" para comprender cómo funciona.
En primer lugar, solo le mostraré el algoritmo para que pueda absorberlo todo (se muestra en C), luego lo veremos más de cerca:
uint64_t estado0 = 1; uint64_t estado1 = 2;
uint64_t xorshift128plus() { uint64_t s1 = estado0; uint64_t s0 = estado1;estado0 = s0;s1 ^= s1 << 23;s1 ^= s1 >> 17;s1 ^= s0;s1 ^= s0 >> 26;estado1 = s1; devuelve estado0 + estado1;}
Si eres como yo (con experiencia en front-end y sin un título en informática), miras esto y piensas "Ok, asignación de variable, asignación de variable, función... bastante simple..." pero luego llegas a s1 ^= s1 << 23;
y decir "¿qué mierda?"
Estos son operadores bit a bit. Manipulan datos a nivel de bits (1 y 0) y forman el corazón y el alma del algoritmo que estamos viendo. También son algo con lo que el desarrollador web promedio rara vez (o nunca) tiene la oportunidad de trabajar. Entonces, para explicar lo que hace este algoritmo, repasaré rápidamente los tres operadores bit a bit que se muestran arriba y cómo funcionan.
El primer operador, <<
, se llama desplazamiento a la izquierda. He aquí un ejemplo: 12 << 4
. En este ejemplo, tomaría una representación binaria del número 12 y la desplazaría a la izquierda 4 lugares; por lo tanto, desplazamiento a la izquierda. Así es como funciona:
El inverso de esto, llamado desplazamiento a la derecha >>
, hace lo mismo pero se desplaza a la derecha en lugar de a la izquierda.
El segundo operador, =^
es el operador de asignación xor. Xor (abreviatura de exclusivo o ) compara las representaciones binarias de dos números y genera un 0 donde los bits correspondientes coinciden y genera un 1 donde los bits correspondientes son diferentes. Puedes pensar en xor como 'uno o el otro pero no ambos '. Aquí hay una visualización de un xor aleatorio 53^18
(xor sin la asignación)
Ahora que sabe lo que hacen todos los operadores, puede comenzar a entender el algoritmo xorshift anterior. Ese bit desconcertante que mencioné anteriormente ( s1 ^= s1 << 23;
) es simplemente desplazar s1 23 lugares a la izquierda y luego hacer xor en el resultado con s1, lo que da como resultado el valor recién asignado de s1. O, en otras palabras, es xor shifting.
Entonces, para simplificar demasiado las cosas, el algoritmo toma dos valores iniciales, los cambia, mezcla sus valores de bits, los pasa a través de una puerta lógica, repite esto varias veces, luego los suma y...
Bam. Obtienes un número 'aleatorio'.
Para empaquetar todo ordenadamente, aquí hay una descripción general.
Pregunta: ¿cómo Math.random()
de JavaScript genera números aleatorios?
Responder:
Entonces, resulta que todo lo que realmente estamos haciendo aquí es tomar algo de información, darle vueltas con algunas matemáticas y escupir un resultado. Un proceso completamente predecible, no aleatorio. Pero uno que nos parezca lo suficientemente aleatorio como para que cumpla sus propósitos como nuestra fuente casual de caos en JavaScript.
Para cualquier persona interesada, tengo una implementación JS de xorshift128+ disponible en GitHub (vinculada a continuación) que le brinda una visualización de la "aleatoriedad" del algoritmo y le permite jugar con valores iniciales y de desplazamiento . ¡Gracias por leer!
lordpoint/xorshift-sandbox-and-visualizer _xorshift-sandbox-and-visualizer: una implementación del algoritmo de generación de números pseudoaleatorios (PRNG) xorshift+…_github.com