Teil von SELFHTML aktuell Teil von Artikel Teil von JavaScript

Gleichverteilte Zufallszahlen erzeugen

nach unten Mathias Schäfer
nach unten Vorbemerkung zur mathematischen Schreibweise
nach unten Erste Schritte mit Math.random()
nach unten Ganzzahlige Zufallswerte mit Hilfe von Math.round()
nach unten Ganzzahlige Zufallswerte mit Hilfe von Math.floor()
nach unten Helferfunktion zur Berechnung ganzzahliger, gleichverteilter Zufallszahlen
nach unten Demonstration

Mathias Schäfer

E-Mail: E-Mail molily@selfhtml.org

Bei Fragen zu diesem Beitrag bitte den Autor des Beitrags kontaktieren!

Der folgende Artikel basiert auf Beiträgen von deutschsprachige Seite Gunnar Bittersmann und deutschsprachige Seite Jürgen Berkemeier im SELFHTML Forum. Vielen Dank an die beiden.

nach obennach unten

Vorbemerkung zur mathematischen Schreibweise

Um Zahlenbereiche zu bezeichnen, verwendet man in der Alltagssprache »von 5 bis 10« oder »zwischen 5 und 10«. Allerdings ist nicht immer eindeutig, was damit gemeint ist und ob die Zahlen am Anfang und Ende noch dazugehören. Daher verwendet dieser Artikel eine gängige mathematische Schreibweise für Zahlenbereiche, sogenannte deutschsprachige Seite Intervalle. Damit lassen sich die Sachverhalte rund um die Erzeugung von Zufallszahlen kompakter beschreiben:

[0;5]
Abgeschlossenes Intervall. Bezeichnet alle Zahlen, die größer oder gleich 0 sind und kleiner oder gleich 5 sind. Eine Zahl x ist Teil des Intervalls, wenn für sie gilt 0 ≤ x ≤ 5. Die Zahl 3.5 ist im Intervall, die Zahl 6 nicht mehr.
[0;5[
Rechtsoffenes Intervall. Bezeichnet alle Zahlen, die größer oder gleich 0 sind, aber kleiner als 5 sind. Eine Zahl x ist Teil des Intervalls, wenn für sie gilt 0 ≤ x < 5. Die Zahl 0.5 ist im Intervall, die Zahl 5 nicht mehr.

Dieser Unterschied wird uns noch mehrfach beschäftigen, daher sollten Sie darauf achten, in welche Richtung die letzte Klammer zeigt. Diese bestimmt, ob die Zahl, die die Obergrenze bildet, noch zum Intervall dazugehört oder nicht.

nach obennach unten

Erste Schritte mit Math.random()

bereichsübergreifende Seite Math.random() liefert gemäß dem ECMAScript-Standard eine Pseudo-Zufallszahl aus dem Intervall [0;1[, das heißt größer gleich 0 und kleiner als 1. Es handelt sich um eine deutschsprachige Seite rationale Zahl, zum Beispiel 0.06631505941813465.

Nun will man in JavaScript äußerst selten eine Zufallszahl aus dem Intervall [0;1[, sondern aus [x;y]. x und y stehen für beliebige Werten wie zum Beispiel 5 und 10. Es gilt also, die von Math.random() gelieferten Wert auf eine anderes Intervall abzubilden.

Der erste Versuch könnte so aussehen:

var min = 5;
var max = 10;
var x = (Math.random() * (max - min)) + min;

Dies liefert eine rationale Zahl x aus dem Intervall [5;10[.

Dies erfüllt zwei übliche Wünsche nicht:

  1. Man will die maximale Zahl (hier 10) nicht aussparen. Man will eine zufällige Zahl aus dem Intervall [5;10], nicht aus dem Intervall [5;10[.
  2. Man will eine deutschsprachige Seite natürliche Zahl. Die Zahl soll aus der Zahlenmenge { 5, 6, 7, 8, 9, 10 } stammen.

nach obennach unten

Ganzzahlige Zufallswerte mit Hilfe von Math.round()

Die übliche Lösung, die diese beiden Anforderungen zunächst erfüllt, sieht so aus:

var x = Math.round(Math.random() * (max - min)) + min;

bereichsübergreifende Seite Math.round() rundet kaufmännisch, das heißt, 5.2 wird 5, 7.2 wird 7 und 9.8 wird 10. Damit bekommen wir eine natürliche Zahl aus dem Intervall [5;10].

Diese Lösung findet sich zwar überall im Web, aber sie ist äußerst problematisch und fehlerhaft.

Will man eine zufällige Zahl aus der Menge { 5, 6, 7, 8, 9, 10 } ziehen, so soll jede Zahl mit gleicher Wahrscheinlichkeit gezogen werden. Der Wahrscheinlichkeitswert einer jeden Zahl aus dieser Menge mit sechs Zahlen wäre 1/6 (ein Sechstel). Das kennen wir von einem sechsseitigen Würfel.

Die obige Lösung mit Math.round() sorgt aber nicht für eine gleichmäßige Wahrscheinlichkeit. Die Zahl 5 wird nicht gleich häufig gezogen wie die Zahl 6.

Der Teilterm Math.random() * (10 - 5) berechnet zunächst eine zufällige rationale Zahl aus dem Intervall [0;5[. Diese Verteilung ist noch gleichmäßig. Das heißt, jede Zahl in diesem Intervall wird mit gleicher Wahrscheinlichkeit gezogen.

Bei der anschließenden Rundung muss nun bedacht werden, welcher Teilintervall zu welchem ganzzahligen Wert führt. Diese Rundung von Zahlen und die anschließende Addition des Minimalwertes soll folgende Tabelle veranschaulichen.

a = Math.random() * (10 - 5) [0; 0.5[ [0.5; 1[ [1; 1.5[ [1.5; 2[ [2; 2.5[ [2.5; 3[ [3; 3.5[ [3.5; 4[ [4; 4.5[ [4.5; 5[
b = Math.round(a) 0 1 2 3 4 5
c = b + 5 5 6 7 8 9 10
Wahrscheinlichkeit 1/10 1/5 1/5 1/5 1/5 1/10

Die Tabelle ist so zu lesen: Der besagte Teilterm liefert eine Zufallszahl a aus dem Intervall [0;5[. Dieser ist in 0.5-er Teilintervallen in Spalten aufgeteilt.

Der springende Punkt ist nun, dass die Rundung die Zahlen aus dem Intevall [0; 0.5[ auf 0 abrundet. Alle Zahlen aus dem Intervall [0.5; 1.5[ werden hingegen auf 1 auf- bzw. abgerundet. Die Menge der Zufallszahlen, die gerundet 1 ergeben, ist also doppelt so groß wie die Menge der Zahlen, die gerundet 0 ergeben. Dasselbe Problem liegt am Ende vor: Lediglich die Zahlen aus dem Intervall [4.5; 5[ ergeben gerundet 5.

Der Effekt dieser Rundung ist eine ungleiche Verteilung der Wahrscheinlichkeiten. Die Zahlen 6, 7, 8 und 9 werden jeweils mit doppelter Wahrscheinlichkeit gezogen als die Zahlen 5 und 10. Die beiden Zahlen am Anfang und am Ende der Zahlenmenge, aus der zufällig gezogen werden soll, sind also benachteiligt in der Ziehung.

nach obennach unten

Ganzzahlige Zufallswerte mit Hilfe von Math.floor()

Lange Rede, kurzer Sinn: Ein anderes Verfahren ist nötig, damit jede Zahl mit gleicher Wahrscheinlichkeit gezogen wird. Eine Rundung ist zwar weiterhin nötig, aber eine, die immer ein Intervall der Länge 1 auf eine ganze Zahl abbildet.

Diese Aufgabe erfüllt bereichsübergreifende Seite Math.floor(). Math.floor() rundet jede Zahl auf die nächstniedrige ganze Zahl ab. Die Berechnung der Zufallszahl wird entsprechend geändert:

var min = 5;
var max = 10;
var x = Math.floor(Math.random() * (max - min)) + min;

Dies gibt uns eine zufällige natürliche Zahl aus dem Intervall [5;10[. Dies fällt hinter die Lösung mit Math.round() in dem Punkt zurück, dass die maximale Zahl 10 wieder ausgespart wird. Daher wird der Term folgendermaßen ergänzt:

var x = Math.floor(Math.random() * (max - min + 1)) + min;

Die korrekte Funktionsweise wird deutlich, wenn wir die gleiche Tabelle mit der verbesserten Lösung aufstellen:

a = Math.random() * (10 - 5 + 1) [0; 1[ [1; 2[ [2; 3[ [3; 4[ [4; 5[ [5; 6[
b = Math.floor(a) 0 1 2 3 4 5
c = b + 5 5 6 7 8 9 10
Wahrscheinlichkeit 1/6 1/6 1/6 1/6 1/6 1/6

Anmerkung: Die Benutzung von Math.round() bei der Erzeugung von zufälligen Ganzzahlen ist kein Übel an sich. Die Lösung mit Math.round() lässt sich abändern, sodass ebenfalls ein Intervall der Länge 1 auf eine ganze Zahl abgebildet wird und die Gleichverteilung gewährleistet ist. Dazu verschiebt man die Zahlen vor der Rundung um 0.5 nach links, also substrahiert 0.5:

var x = Math.round(Math.random() * (max - min + 1) - 0.5) + min;

Dies sei hier nur der Vollständigkeit erwähnt. Um Verwirrung bei verschiedenen Lösungen mit Math.round() zu vermeiden, wird hier die Umsetzung mit Math.floor() favorisiert.

nach obennach unten

Helferfunktion zur Berechnung ganzzahliger, gleichverteilter Zufallszahlen

Eine allgemeine Helferfunktion, die eine Zufallszahl aus dem Intervall [min;max] zurückgibt, könnte so aussehen:

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

Diese Funktion verhält sich wie rand() in der Programmiersprache PHP. Um eine zufällige ganze Zahl größer gleich 5 und kleiner gleich 10, also aus der Zahlenmenge { 5, 6, 7, 8, 9, 10 } zu ziehen, rufen wir rand(5, 10) auf.

nach obennach unten

Demonstration

Bei 120 000 Ziehungen sollte jede Zahl aus der Menge { 5, 6, 7, 8, 9, 10 } im statistischen Mittel 20 000 mal gezogen werden, denn der Wahrscheinlichkeitswert liegt bei 1/6, also 0.16̅ (Periode 6, das heißt 0,16666... usw.). Klicken Sie auf »Berechnung starten«, um die beiden Lösungen zu vergleichen. Die angezeigten Wahrscheinlichkeiten sind gerundet. Sie können den Test auch mehrfach durchführen, um genauere Resultate zu bekommen. Sie werden das Ungleichgewicht bei den Zahlen 5 und 10 auf der Seite der Lösung mit Math.round() feststellen.

Math.round() Math.floor()
Zahl Ziehungen Wahrscheinlichkeit Ziehungen Wahrscheinlichkeit

Teil von SELFHTML aktuell Teil von Artikel Teil von JavaScript

© 2008 bereichsübergreifende Seite Impressum, für diese Seite: E-Mail molily@selfhtml.org