Die Mischung, die keine ist
Der häufigste Randomisierungsfehler in JavaScript:
const shuffled = items.sort(() => Math.random() - 0.5); // FALSCH
Es sieht gut aus und ist überall zu finden. Es ist keine Mischung. Sort-Comparatoren müssen konsistent sein, und dieser ist es nicht, daher produziert die Engine-Sortierung verzerrte Permutationen.
Gemessen an vier Quizoptionen:
| Position | Wahrscheinlichkeit, dass die richtige Antwort dort landet |
|---|---|
| 0 | 28,3% |
| 1 | 28,2% |
| 2 | 24,7% |
| 3 | 18,8% |
Gleichverteilt wären 25% pro Position. In der Praxis schlug das ständige Raten der ersten Option zufälliges Raten – ein Spieler, der dies bemerkt, hätte einen Vorteil, und das Quiz war still und leise unfair.
Die Lösung
Fisher-Yates, einmal, in einer Hilfsfunktion:
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;
}
Dann ersetze jede Fundstelle. Der Fehler tritt meist mehr als einmal auf – eine Codebasis hatte ihn an elf Stellen: Mischen von Quizoptionen, Auswählen, welche Fragen erscheinen, Auswählen, welche Artikel angeboten werden, Sortieren von Menüauswahlen. Jede einzelne verzerrte etwas leicht.
Ersetze nicht blind auf Basis eines Musters. (Math.random() - 0.5) taucht auch
legitim als Rauschen in Physik und Jitter auf:
velocity += (Math.random() - 0.5) * 0.012; // das in Ruhe lassen
Überprüfe, ob jeder Treffer tatsächlich ein Sort-Comparator ist.
Teste die Verteilung
Vertraue nicht auf bloßes Anschauen. Zähle sie:
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) + '%'));
Eine Streuung von mehr als etwa 3 Prozentpunkten über die Positionen hinweg bedeutet, dass etwas verzerrt ist.
Verwende einen ordentlichen Generator für den Test. Ein selbstgestrickter linearer Kongruenzgenerator
produzierte 1–2 Punkte „Fehler“, die wie ein echter Bug aussahen, aber die Schuld des Tests waren.
Math.random() ist gleichmäßig genug, um Verteilungen zu prüfen – hebe den benutzerdefinierten Generator
für Fälle auf, in denen reproduzierbare Seeds benötigt werden, und validiere ihn separat.
Überprüfe die angezeigten Chancen
Wenn du irgendwo Wahrscheinlichkeiten anzeigst – einen Prozentsatz, einen Fortschrittsbalken, eine Erfolgswahrscheinlichkeit – stelle sicher, dass sie mit dem übereinstimmen, was der Code tatsächlich würfelt. Simuliere den echten Wurf ein paar hunderttausend Mal und vergleiche:
cred 0 gezeigt 5,0%/40,0%/55,0% tatsächlich 5,0%/40,0%/55,0%
cred 100 gezeigt 40,0%/40,0%/20,0% tatsächlich 40,0%/40,1%/20,0%
Ein maximaler Fehler unter einem halben Prozentpunkt bedeutet, dass die Anzeige nicht lügt. Eine gezeigte Wahrscheinlichkeit, die das Ergebnis nicht einhält, ist schlimmer, als nichts zu zeigen, weil Leute Entscheidungen auf Basis dieser Informationen treffen.
Solange du dabei bist
Überprüfe, ob ein genanntes Ziel tatsächlich erreichbar ist. Ein Zähler, der „3 von 5“ anzeigt, sagt den Spielern, dass 5 das Ziel ist; wenn 5 mit einer Wahrscheinlichkeit von 0,17% eintritt, verspricht die Oberfläche etwas, das praktisch nie passiert. Berechne die tatsächlichen Chancen für jeden Meilenstein, den du anzeigst.