El barajo que no lo es
El error de aleatorización más común en JavaScript:
const shuffled = items.sort(() => Math.random() - 0.5); // INCORRECTO
Parece correcto y está por todas partes. No es un barajo. Se espera que los comparadores de sort sean
consistentes, y este no lo es, por lo que el algoritmo de ordenamiento del motor produce
permutaciones sesgadas.
Medido en cuatro opciones de un cuestionario:
| Posición | Probabilidad de que la respuesta correcta caiga ahí |
|---|---|
| 0 | 28.3% |
| 1 | 28.2% |
| 2 | 24.7% |
| 3 | 18.8% |
Si fuera uniforme, sería 25% cada una. En la práctica, adivinar siempre la primera opción superó adivinar al azar — un jugador que lo notara tendría ventaja, y el cuestionario era silenciosamente injusto.
La corrección
Fisher–Yates, una vez, en una función auxiliar:
function shuffle(a) {
const out = [...a];
for (let i = out.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[out[i], out[j]] = [out[j], out[i]];
}
return out;
}
Luego reemplázalo en todos los sitios. El error suele aparecer más de una vez — un código fuente lo tenía en once lugares: barajando opciones de un cuestionario, eligiendo qué preguntas aparecían, seleccionando qué artículos se ofrecían, ordenando opciones de menú. Cada uno sesgaba algo ligeramente.
No reemplaces indiscriminadamente buscando el patrón. (Math.random() - 0.5) también aparece
legítimamente como ruido en física y en vibración:
velocity += (Math.random() - 0.5) * 0.012; // deja esto como está
Verifica que cada coincidencia sea realmente un comparador de sort.
Prueba la distribución
No evalúes la aleatoriedad visualmente. Cuéntala:
const pos = [0,0,0,0], N = 200000;
for (let i = 0; i < N; i++) {
const order = shuffle(['a','b','c','d'].map((t,i) => [t,i]));
pos[order.findIndex(o => o[1] === 0)]++;
}
pos.forEach((c,i) => console.log(i, (c/N*100).toFixed(1) + '%'));
Una dispersión superior a aproximadamente 3 puntos porcentuales entre las posiciones significa que algo está sesgado.
Usa un generador adecuado para la prueba. Un generador lineal congruencial hecho a mano
produjo "errores" de 1 a 2 puntos que parecían un error real pero eran culpa de la propia prueba.
Math.random() es lo suficientemente uniforme para verificar distribuciones — guarda el generador
personalizado para cuando necesites semillas reproducibles, y valídalo por separado.
Verifica las probabilidades que muestras
Si muestras probabilidades en algún lugar — un porcentaje, una barra de progreso, una probabilidad de éxito — asegúrate de que coincidan con lo que realmente lanza el código. Simula la tirada real unos cientos de miles de veces y compara:
cred 0 mostrado 5.0%/40.0%/55.0% real 5.0%/40.0%/55.0%
cred 100 mostrado 40.0%/40.0%/20.0% real 40.0%/40.1%/20.0%
Un error máximo menor a medio punto porcentual significa que la pantalla no miente. Una probabilidad mostrada que el resultado no respeta es peor que no mostrar nada, porque las personas toman decisiones basadas en ella.
Ya que estás ahí
Verifica que un objetivo declarado sea realmente alcanzable. Un contador que dice "3 de 5" les indica a los jugadores que 5 es la meta; si 5 ocurre el 0.17% de las veces, la interfaz está prometiendo algo que efectivamente nunca sucede. Calcula las probabilidades reales de cada hito que muestres.