Math.random () versus Random.nextInt (int)

135

Was ist der Unterschied zwischen Math.random() * nund Random.nextInt(n)wo nist eine ganze Zahl?

Gili
quelle
Ich kenne die Mathematik nicht, aber ich weiß, dass FindBugs sich beschwert, wenn Sie verwendenMath.random()
finnw
3
Denken Sie daran, dass Random keine statische Methode hat. Verwenden Sie daher: (new Random ()). NextInt (n)). Damit Math eine ähnliche Ganzzahl generiert, verwenden Sie: Math.floor ((Math.random () * n) +1);
Dimitri Dewaele

Antworten:

169

Hier ist die detaillierte Erklärung, warum " Random.nextInt(n)sowohl effizienter als auch weniger voreingenommen ist als Math.random() * n" aus dem Beitrag der Sun-Foren, mit dem Gili verlinkt hat:

Math.random () verwendet intern Random.nextDouble ().

Random.nextDouble () verwendet Random.next () zweimal, um ein Double zu generieren, dessen Mantisse ungefähr gleichmäßig verteilte Bits enthält, sodass es gleichmäßig im Bereich von 0 bis 1- (2 ^ -53) verteilt ist.

Random.nextInt (n) verwendet Random.next () im Durchschnitt weniger als zweimal - es wird einmal verwendet, und wenn der erhaltene Wert über dem höchsten Vielfachen von n unter MAX_INT liegt, wird es erneut versucht, andernfalls wird der Wert modulo n (this) zurückgegeben verhindert, dass die Werte über dem höchsten Vielfachen von n unter MAX_INT die Verteilung verzerren), wodurch ein Wert zurückgegeben wird, der gleichmäßig im Bereich von 0 bis n-1 verteilt ist.

Vor der Skalierung um 6 ist die Ausgabe von Math.random () einer von 2 ^ 53 möglichen Werten, die aus einer gleichmäßigen Verteilung gezogen werden.

Durch Skalieren mit 6 wird die Anzahl der möglichen Werte nicht geändert, und durch Umwandeln in ein int werden diese Werte in einen von sechs "Buckets" (0, 1, 2, 3, 4, 5) gezwungen, wobei jeder Bucket den Bereichen entspricht, die beide umfassen 1501199875790165 oder 1501199875790166 der möglichen Werte (da 6 kein Disvisor von 2 ^ 53 ist). Dies bedeutet, dass sich für eine ausreichende Anzahl von Würfeln (oder einen Würfel mit einer ausreichend großen Anzahl von Seiten) der Würfel in Richtung der größeren Eimer vorgespannt zeigt.

Sie werden sehr lange darauf warten, dass dieser Effekt angezeigt wird.

Math.random () erfordert ebenfalls etwa die doppelte Verarbeitung und unterliegt der Synchronisation.

matt b
quelle
3
Random.nextInt und nextDouble unterliegen ebenfalls der Synchronisation.
Adrianos
Was bedeutet in diesem Zusammenhang bitte "weniger voreingenommen"?
ΦXocę 웃 Пepeúpa ツ
2
@ ΦXocę 웃 Пepeúpa ツ Es bedeutet einfach, dass bestimmte Zahlen eher gezogen werden als andere. Wie in ist es voreingenommen, einige Zahlen über andere zu wählen (daher nicht völlig zufällig oder bei ausreichender Stichprobengröße einheitlich)
Ford Präfekt
1
Beachten Sie, dass der letzte Kommentar zu diesem Thread lautet: "Die beschriebene Verzerrung ist ein Teil von 2 ^ 53, aber die maximale Zykluslänge des verwendeten PRNG beträgt nur 2 ^ 48. In der Anwendung werden also die Daten angezeigt Verteilung des zugrunde liegenden PRNG, nicht der Verzerrung. " Dies würde auf die Tatsache hinweisen, dass die beiden Methoden gleichwertig sind
digitale Illusion
1
@ ΦXocę 웃 Пepeúpa ツ Ersetzen Sie 6durch 5einen Würfelwürfel: Es wird "5-voreingenommen" sein. Sie können die Würfel ein paar Mal werfen, bevor Sie feststellen, dass etwas mit den Würfeln nicht stimmt. Sie sind gezwungen, eine äußerst anspruchsvolle gründliche Prüfung durchzuführen, bevor Sie feststellen, dass mit einem Zufallsgenerator etwas nicht stimmt.
Dávid Horváth
27

Ein weiterer wichtiger Punkt ist, dass Random.nextInt (n) wiederholbar ist, da Sie zwei zufällige Objekte mit demselben Startwert erstellen können . Dies ist mit Math.random () nicht möglich.

dfa
quelle
0

Nach diesem Beispiel Random.nextInt(n)hat weniger vorhersehbare Ausgabe als Math.random () * n. Laut [sortiertes Array schneller als ein unsortiertes Array] [1] können wir sagen, dass Random.nextInt (n) schwer vorherzusagen ist .

usingRandomClass: Zeit: 328 Meilensekunde.

usingMathsRandom: Zeit: 187 Meilensekunde.

package javaFuction;
import java.util.Random;
public class RandomFuction 
{
    static int array[] = new int[9999];
    static long sum = 0;
    public static void usingMathsRandom() {
        for (int i = 0; i < 9999; i++) {
         array[i] = (int) (Math.random() * 256);
       }

        for (int i = 0; i < 9999; i++) {
            for (int j = 0; j < 9999; j++) {
                if (array[j] >= 128) {
                    sum += array[j];
                }
            }
        }
    }

    public static void usingRandomClass() {
        Random random = new Random();
        for (int i = 0; i < 9999; i++) {
            array[i] = random.nextInt(256);
        }

        for (int i = 0; i < 9999; i++) {
            for (int j = 0; j < 9999; j++) {
                if (array[j] >= 128) {
                    sum += array[j];
                }
            }

        }

    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        usingRandomClass();
        long end = System.currentTimeMillis();
        System.out.println("usingRandomClass " + (end - start));
        start = System.currentTimeMillis();
        usingMathsRandom();
        end = System.currentTimeMillis();
        System.out.println("usingMathsRandom " + (end - start));

    }

}
Jatin Goyal
quelle
1
In der zweiten Schleife prüfen Sie, ob> = 50 ist, was in mehr als 50% der Fälle der Fall ist. Dies führt dazu, dass diese if-Aussage meistens wahr ist, was sie vorhersehbarer macht. Ihre Ergebnisse sind daher zugunsten Ihrer Antwort voreingenommen
Neuron
Es ist ein Tippfehler ... Wenn Sie im zweiten Beispiel 128 machen, erhalten Sie das gleiche Ergebnis.
Jatin Goyal