Hoy toca un cacharro pequeño: un modo repaso de tarjetas para las preguntas de guardia del equipo. Treinta tarjetas, pregunta delante, respuesta detrás, espacio para pasar. Un fichero suelto, una tarde. Nada que justifique montar un repo.
Al tercer día de usarlo alguien lo dijo en voz alta: «siempre empieza por las mismas». Y tenía razón. El mazo salía distinto cada vez —se notaba que algo se movía— pero el arranque caía una y otra vez en la misma zona.
La línea que hemos copiado todos
El sospechoso estaba a la vista: cartas.sort(() => Math.random() - 0.5). El truco de una línea que aparece en media internet. El problema es que un comparador aleatorio no es un comparador: incumple la única regla que un sort da por supuesta, que si a va antes que b, b vaya después de a, siempre, las veces que haga falta preguntarlo.
Con una regla incoherente no obtienes una permutación uniforme: obtienes lo que salga del algoritmo concreto del motor. Y para arrays cortos los motores tiran de inserción, que mueve poco. De ahí la sensación exacta que teníamos: se desordena, pero cerca.
No es que baraje mal. Es que no está barajando: está ordenando con un criterio que no existe.
El ojo no sirve para juzgar el azar
Aquí es donde la IA en local se gana el sueldo. Le pasé el fichero entero y le pedí un arnés de conteo: barajar cien mil veces un array del 0 al 9 y sacar la matriz de frecuencias posición × valor. En un mazo uniforme, todas las casillas deberían rondar el mismo número.
La matriz salió con la diagonal claramente caliente: cada carta tendía a quedarse cerca de donde ya estaba. Dejó de ser una sensación de pasillo y pasó a ser una tabla. Eso es lo que convierte un «me parece que...» en un bug con pruebas.
Fisher-Yates y el +1 que no es opcional
La solución es de manual: recorrer el array desde el final e intercambiar cada elemento con uno elegido entre 0 e i, incluido. El detalle fino está justo ahí: hay que escribir Math.floor(Math.random() * (i + 1)). Si te comes el +1, prohíbes que un elemento se quede en su sitio y vuelves a sesgar el mazo, esta vez de forma más sutil y bastante más difícil de ver.
Le pedí que me lo justificara contando permutaciones, y la respuesta fue la que quería oír: con el +1 salen n! resultados equiprobables; sin él, (n-1)!, que no reparte bien. Volví a pasar el arnés. Matriz plana.
Lo que vino después, y dónde pasó todo
- Un generador sembrado (mulberry32) para repetir una sesión exacta y tener tests que no parpadeen.
- El arnés de frecuencias, guardado como test de verdad: si alguien vuelve a meter el atajo de una línea, salta.
Y el detalle que importa: esas tarjetas eran preguntas internas de guardia. Nombres de servicios, decisiones de arquitectura, incidencias con fecha. Para que la IA ayudara de verdad tuvo que ver el fichero completo, contenido incluido, más las tablas del arnés. Todo eso se quedó dentro del navegador, sobre mi propia GPU. No hay un servidor al otro lado que sepa cómo se llama nuestro sistema de colas. Y no fue una decisión de privacidad: fue simplemente el sitio donde estaba escribiendo.
Abre Elffuss Code en una pestaña y pégale el fichero entero —datos reales incluidos—: el modelo corre dentro de tu navegador, así que ni el código ni el prompt suben a ningún sitio.
Abrir Code →