Javascripts Math.random() – wenn Zufall keiner ist


Gestern schrieb ich eine Funktion in Javascript, die zufällige Ganzzahlen in einem bestimmten Bereich liefern sollte; also genauso wie die PHP-Funktion rand($min, $max). Nach einigen Testläufen bemerkte ich jedoch, daß die Zahlen nicht ganz so zufällig waren wie sie sein sollten; manche kamen öfter vor als erwartet, andere seltener. Es stellte sich heraus, daß das Runden mit Math.round() der Übeltäter war.

Math.round() – doch keine so runde Sache

Angenommen, man will eine Zufallszahl zwischen 1 und 10 generieren. Das könnte etwa so aussehen:

randomNumber = Math.round(Math.random() * 9) + 1;

Das erwartete Ergebnis wäre, daß alle Zahlen mit einer Wahrscheinlichkeit von 1/10 (oder 10%) vorkommen werden. Tun sie aber leider nicht. Stattdessen fallen die 1 und die 10 seltener als die restlichen Zahlen. Warum ist das so?
Ganz einfach: Das Ergebnis von Math.random()*9 kann jeden Wert zwischen 0 und 8,9 annehmen. Und jetzt sehen wir uns mal an, wie die Werte anschließend gerundet werden:

0 – 0,49 0,5 – 1,49 1,5 – 2,49 2,5 – 3,49 3,5 – 4,49 4,5 – 5,49 5,5 – 6,49 6,5 – 7,49 7,5 – 8,49 8,5 – 8,99
0 1 2 3 4 5 6 7 8 9

Wie man anhand dieser Tabelle erkennt (ich habe versucht, es auch optisch mithilfe der Spaltenbreiten zu verdeutlichen), hat nur jeweils ein Intervall von 0,5 die Chance, zu einer 0 oder 9 gerundet zu werden, während es bei allen anderen Zahlen ein Intervall von 1 ist. Das erklärt, wieso das gewünschte Minimum und Maximum nur halb so häufig auftreten wie die restlichen Zahlen. Aber wie soll man denn sonst die Kommastellen loswerden?

Math.floor() ist Dein Freund! :)

Genau, man rundet die Zahl einfach grundsätzlich ab. Doch halt! Was ist dann mit der 10? Die wird ja dann gar nicht mehr auftauchen, weil der höchstmögliche Wert von Math.random()*9 immer zu einer 8 abgerundet wird! Stimmt – deshalb erhöht man den Multiplikator einfach um 1:

randomNumber = Math.floor(Math.random() * 10) + 1;

Jetzt fällt die Verteilung der Zufallszahlen so aus wie erwartet, nämlich annähernd gleichmäßig. Mit diesem Wissen kann man sich jetzt endlich eine korrekt funktionierende, einfach zu bedienende, PHP-ähnliche Random-Funktion schreiben:

function rand(min, max) {
	return Math.floor(Math.random() * (max - min + 1)) + min;
}

P.S.: Es gibt keinen Seed-Parameter!

Es hält sich hartnäckig das Gerücht, man könne der random()-Funktion einen Wert übergeben (einen sogenannten „Seed“-Parameter), den sie als Grundlage für die Zufallsberechnung verwenden soll. So hat es sich anscheinend eingebürgert, der Funktion die aktuellen Sekunden oder Millisekunden zu übergeben in der Hoffnung, daß dadurch die Zahlen irgendwie „zufälliger“ werden:

now = new Date();
seed = now.getMilliseconds();
randomNumber = Math.floor(Math.random(seed) * 10) + 1

Tja … Leider ist das Schwachsinn. :) Ein solcher Parameter existiert nicht. Man wird ihn auch in keiner Javascript-Dokumentation finden. Jeder Wert, den man der Funktion übergibt, wird schlichtweg ignoriert. (Die Konstruktion mit der Zeit ist darüber hinaus aber besonders unsinnig, weil Math.random() sowieso immer die aktuelle Zeit als Rechengrundlage benutzt.)


Eine Antwort zu “Javascripts Math.random() – wenn Zufall keiner ist”

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert