Le mélange qui n'en est pas un
Le bug de randomisation le plus courant en JavaScript :
const shuffled = items.sort(() => Math.random() - 0.5); // FAUX
Il a l'air correct et on le trouve partout. Ce n'est pas un mélange. Les comparateurs de tri sont censés être cohérents, et celui-ci ne l'est pas, donc le tri du moteur produit des permutations biaisées.
Mesuré sur quatre options de quiz :
| Position | Probabilité que la bonne réponse s'y trouve |
|---|---|
| 0 | 28,3 % |
| 1 | 28,2 % |
| 2 | 24,7 % |
| 3 | 18,8 % |
Une distribution uniforme donnerait 25 % chacun. En pratique, deviner toujours la première option battait le hasard — un joueur qui le remarquait aurait un avantage, et le quiz était discrètement injuste.
La correction
Fisher-Yates, une fois, dans une fonction d'aide :
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;
}
Ensuite, remplacez chaque occurrence. Le bug apparaît généralement plus d'une fois — une base de code l'avait dans onze endroits : mélange des options de quiz, choix des questions affichées, sélection des articles proposés, ordre des éléments de menu. Chacun biaisait légèrement quelque chose.
Ne remplacez pas en bloc sur une simple correspondance de motif. (Math.random() - 0.5) apparaît aussi légitimement comme bruit en physique ou comme tremblement :
velocity += (Math.random() - 0.5) * 0.012; // laissez cela tranquille
Vérifiez que chaque occurrence est bien un comparateur de tri.
Tester la distribution
Ne vous fiez pas à une inspection visuelle du hasard. Comptez-le :
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) + '%'));
Un écart supérieur à environ 3 points de pourcentage entre les positions signifie que quelque chose est biaisé.
Utilisez un générateur correct pour le test. Un générateur congruentiel linéaire artisanal a produit des « erreurs » de 1 à 2 points qui ressemblaient à un vrai bug, mais qui venaient en fait du test lui-même. Math.random() est suffisamment uniforme pour vérifier les distributions — réservez votre générateur personnalisé pour les cas où vous avez besoin de graines reproductibles, et validez-le séparément.
Vérifier les probabilités que vous affichez
Si vous affichez des probabilités quelque part — un pourcentage, une barre de progression, une chance de succès — assurez-vous qu'elles correspondent à ce que le code tire réellement. Simulez le tirage réel plusieurs centaines de milliers de fois et comparez :
cred 0 affiché 5,0 %/40,0 %/55,0 % réel 5,0 %/40,0 %/55,0 %
cred 100 affiché 40,0 %/40,0 %/20,0 % réel 40,0 %/40,1 %/20,0 %
Une erreur maximale inférieure à un demi-point de pourcentage signifie que l'affichage ne ment pas. Une probabilité affichée que le résultat ne respecte pas est pire que de n'afficher rien, car les gens prennent des décisions basées sur elle.
Pendant que vous y êtes
Vérifiez qu'un objectif annoncé est réellement atteignable. Un compteur indiquant « 3 sur 5 » dit aux joueurs que 5 est la cible ; si 5 n'arrive que 0,17 % du temps, l'interface promet quelque chose qui n'arrive effectivement jamais. Calculez les véritables probabilités de chaque jalon que vous affichez.