¿Las casualidades no son casuales? Cómo hacer que JavaScript genere números "justos"
\n\nImagine: está escribiendo un juego donde el enemigo aparece en un punto aleatorio de la pantalla. O está creando una prueba A/B que distribuye uniformemente a los usuarios en grupos. O simplemente quiere generar una contraseña aleatoria. En todos los casos, necesitará la misma magia: generar un número aleatorio entre dos valores en JavaScript.
\n\nParecería que podría ser más simple? Math.random() y listo. Pero, como muestra la pregunta popular en Stack Overflow (millones de visitas, miles de votos), el diablo está en los detalles. Una implementación incorrecta puede provocar sesgos (bias) en la distribución, ralentizaciones en el rendimiento y errores inesperados.
En este artículo no solo le daremos el código listo. Analizaremos su anatomía, aprenderemos a optimizarlo para diferentes escenarios y entenderemos cómo hacer que la generación sea lo más eficiente posible. ¡Allá vamos!
\n\nFundamentos: Math.random() y por qué no es suficiente por sí solo
\n\nEn el corazón de cualquier generación de números aleatorios en JS está el método Math.random(). Devuelve un número pseudoaleatorio de punto flotante del rango [0, 1) (incluye el 0, pero nunca incluye el 1).
console.log(Math.random()); // 0.234567890123456\nconsole.log(Math.random()); // 0.987654321098765\n\nEsta es una excelente base, pero es inútil si necesita un número del 5 al 10 o del -100 al 100. Necesitamos una función para generar un número aleatorio en un rango específico.
\n\nLa respuesta canónica de Stack Overflow
\n\nLa solución más popular y correcta es la siguiente:
\n\nfunction getRandomArbitrary(min, max) {\n return Math.random() * (max - min) + min;\n}\n\n- \n
- Cómo funciona:
(max - min)es la anchura del rango. Multiplicando porMath.random(), obtenemos un número de 0 a la anchura. Sumandomin, desplazamos el resultado al rango deseado. \n - Resultado: Un número decimal desde min (incluido) hasta max (excluido). \n
Pero, ¿qué pasa si necesita un número entero? Aquí es donde entran en juego Math.floor(), Math.ceil() y Math.round().
Generación de un número entero aleatorio: El diablo en el redondeo
\n\nLa segunda parte de la respuesta en Stack Overflow es la función para números enteros:
\n\nfunction getRandomInt(min, max) {\n min = Math.ceil(min);\n max = Math.floor(max);\n return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n\nObserve el + 1 al final. Este es un detalle críticamente importante. Sin él, el valor máximo max nunca aparecerá, porque Math.random() nunca devuelve exactamente 1.
¿Por qué Math.ceil() y Math.floor()?
\nEs una protección contra errores. Si alguien pasa a la función min = 1.5, Math.ceil(1.5) lo convierte en 2. Y Math.floor(9.9) lo convierte en 9. Esto garantiza una entrada entera y un resultado predecible.
Optimización del rendimiento: Comparando enfoques
\n\nAhora viene lo más interesante: el rendimiento de la generación de números aleatorios en JavaScript. Supongamos que necesita crear 10 000 puntos aleatorios en un canvas. Cada milisegundo cuenta.
\n\nProbamos tres enfoques:
\n\n| Enfoque | \nCódigo | \nTiempo (10 millones de iteraciones) | \n
|---|---|---|
| Clásico (con redondeo) | \nMath.floor(Math.random() * (max - min + 1)) + min | \n ~180 ms | \n
| Con OR bit a bit (truco) | \n(Math.random() * (max - min +
|