Wie funktioniert Math.random() in Java?

2 Antworten

Die Math-Klasse scheint einfach ein neues java.util.Random Objekt 1x zu erstellen und dann von dort die Werte abzufragen (.nextDouble()).

(per Decompilerplugin in Eclipse "herausgefunden")

Die Methode Math.random() sieht so aus:

public static synchronized double random()
{
if (rand == null)
rand = new Random();
return rand.nextDouble();
}

Es wird also auf die Klasse Random mit folgenden zu beachtenden Konstruktorn zurückgegriffen:

public Random()
{
this(System.currentTimeMillis());
}
public Random(long seed)
{
setSeed(seed);
}

Im Standardkonstruktor der Klasse Random wird der Wert System.currentTimeMillis() an den entsprechenden Konstruktor übergeben, der wiederum die Methode setSeed(...) aufruft, die ausprogrammiert so aussieht:

public synchronized void setSeed(long seed){
this.seed = (seed ^ 0x5DEECE66DL) & ((1L << 48) - 1);
haveNextNextGaussian = false;
}

In der Klasse Random wird also die Instanzvariable seed sowie haveNextNextGaussian definiert. Nun wird in der obigen Methode Math.randon() auf dem Random-Objekt rand die Methode nextDouble() aufgerufen, die in der Random-Klasse folgendermaßen implementiert ist:

public double nextDouble()
{
return (((long) next(26) << 27) + next(27)) / (double) (1L << 53);
}

Hier wird dann die Methode next(...) aufgerufen und haufenweise bitweise verschoben, Datentypen umgewandelt und dividiert.

Die Implementierung der Methode Random.next(...) sieht so aus:

protected synchronized int next(int bits)
{
seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
return (int) (seed >>> (48 - bits));
}

Auch hier wird wieder bitweise verschoben und die Variable seed verändert.

Letztendlich wird

(int) (seed >>> (48 - bits))

zurückgegeben, das ist deine (Pseudo-)Zufallszahl. Natürlich ist die Zahl nicht zufällig, wir wissen ja konkret, wie sie erzeugt wird, also ist nichts dem Zufall überlassen. Aber sie verhält sich zumindest wie eine Zufallszahl und das reicht in den meisten Fällen schon.

Die mathematischen Operationen wie die bitweisen Verschiebungen sind eher kompliziert und auch nur zu verstehen, wenn du das binäre Zahlensystem beherrscht. Du kannst mal versuchen, die Operationen nachzuvollziehen, es ist aber wie gesagt kompliziert im Verständnis.

LG Willibergi

Woher ich das weiß:Berufserfahrung – Software-Entwicklung

ceevee  29.03.2017, 17:33

Die Operationen sind eigentlich gar nicht so kompliziert, wenn man sie mal auseinandertüdelt. Durch die ganzen Binärverschiebungen läuft der Algorithmus zwar schneller, die eigentliche Mathematik dahinter wird aber verborgen. Letztendlich läuft es auf einen linearen Kongrenzgenerator hinaus, der seed ist einfach ein Startwert. Das Ziel des Algorithmus ist es, eine gleichverteilte Reihe von Zufallszahlen zu generieren, seed ist quasi der 0te Wert.

https://en.wikipedia.org/wiki/Linear\_congruential\_generator

seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);

1L entspricht binär 1 und dezimal 2^0 = 1. Wenn ich das um 48 Bits nach links verschiebe, dann ist das einfach 2^48. Der Teil hinter dem "&" ist also einfach (2^48 - 1). Das & wiederum ist eine Performanceoptimierung des Modulo.

https://en.wikipedia.org/wiki/Modulo\_operation#Performance\_issues

Die beiden Hex-Zahlen sind Konstanten, die kann man in Dezimalzahlen umwandeln. Die ganze Formel lautet also eigentlich 

seed = (seed * 25214903917 + 11) mod 2^48;

Die darauf folgende Zeile nimmt anscheinend lediglich die Bits 47 ... 15 aus dem Ergebnis und das ist dann die neue Zufallszahl. Und für die nächste Zufallszahl stopfe ich dieses Ergebnis dann wieder als seed in den Algorithmus.

Aber interessante Antwort. Toll, dass du mal nachgeschaut hast. :)

5