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

135

Jaka jest różnica między Math.random() * ni Random.nextInt(n)gdzie njest liczba całkowita?

Gili
źródło
Nie znam matematyki, ale wiem, że FindBugs narzeka, jeśli używaszMath.random()
finnw
3
Pamiętaj, że Random nie ma metody statycznej, więc użyj: (new Random ()). NextInt (n)). Aby Math wygenerował podobną liczbę całkowitą, użyj: Math.floor ((Math.random () * n) +1);
Dimitri Dewaele

Odpowiedzi:

169

Oto szczegółowe wyjaśnienie, dlaczego „ Random.nextInt(n)jest zarówno bardziej wydajne, jak i mniej stronnicze niż Math.random() * n” z postu na forach firmy Sun, do którego prowadzi Gili:

Math.random () używa Random.nextDouble () wewnętrznie.

Random.nextDouble () używa Random.next () dwukrotnie, aby wygenerować double, który ma w przybliżeniu równomiernie rozłożone bity w mantysie, więc jest równomiernie rozłożony w zakresie od 0 do 1- (2 ^ -53).

Random.nextInt (n) używa Random.next () średnio mniej niż dwa razy - używa go raz, a jeśli uzyskana wartość jest powyżej najwyższej wielokrotności n poniżej MAX_INT, próbuje ponownie, w przeciwnym razie zwraca wartość modulo n (to zapobiega przekrzywianiu rozkładu wartości powyżej najwyższej wielokrotności n poniżej wartości MAX_INT), w związku z czym zwraca wartość, która jest równomiernie rozłożona w zakresie od 0 do n-1.

Przed skalowaniem o 6 wynik funkcji Math.random () jest jedną z 2 ^ 53 możliwych wartości pobranych z rozkładu równomiernego.

Skalowanie o 6 nie zmienia liczby możliwych wartości, a rzutowanie na int następnie wymusza te wartości w jednym z sześciu „przedziałów” (0, 1, 2, 3, 4, 5), z których każdy odpowiada zakresom obejmującym albo 1501199875790165 lub 1501199875790166 z możliwych wartości (ponieważ 6 nie jest doradcą 2 ^ 53). Oznacza to, że przy wystarczającej liczbie rzutów kośćmi (lub kości z dostatecznie dużą liczbą boków), kość okaże się odchylona w kierunku większych koszy.

Będziesz bardzo długo czekać, aż ten efekt się pojawi.

Math.random () również wymaga około dwukrotnie większego przetwarzania i podlega synchronizacji.

matowe b
źródło
3
Synchronizacji podlegają również Random.nextInt i nextDouble.
adrianos
Co w tym kontekście oznacza „mniej stronniczy”?
ΦXocę 웃 Пepeúpa ツ
2
@ ΦXocę 웃 Пepeúpa ツ Oznacza to po prostu, że niektóre liczby mają większe szanse na wylosowanie niż inne. Ponieważ jest tendencyjny do wybierania niektórych liczb w stosunku do innych (stąd nie jest całkowicie losowy lub ma jednolity rozmiar próbki)
ford prefect
1
Zwróć uwagę, że ostatni komentarz do tego wątku informuje: „Opisane odchylenie jest jedną częścią w 2 ^ 53, ale maksymalna długość cyklu używanego PRNG to tylko 2 ^ 48. Więc to, co zobaczysz w aplikacji, to dane dystrybucja bazowego PRNG, a nie stronniczość. " To wskazywałoby na fakt, że te dwie metody są równoważne
iluzja cyfrowa
1
@ ΦXocę 웃 ПepeúpaツWymień 6ze 5na sześcian: będzie to „5-tendencyjne”. Możesz rzucić kostką kilka razy, zanim zauważysz, że coś jest nie tak z kostką. Jesteś zmuszony przeprowadzić niezwykle wyrafinowane, dokładne badanie, zanim zauważysz, że coś jest nie tak z generatorem losowym.
Dávid Horváth
27

kolejną ważną kwestią jest to, że Random.nextInt (n) jest powtarzalne, ponieważ możesz utworzyć dwa obiekty Random za pomocą samym ziarnem. Nie jest to możliwe z Math.random ().

dfa
źródło
0

Zgodnie z tym przykładem Random.nextInt(n)ma mniej przewidywalne wyniki niż Math.random () * n. Zgodnie z [posortowana tablica szybciej niż nieposortowana tablica] [1] Myślę, że możemy powiedzieć, że Random.nextInt (n) jest trudna do przewidzenia .

usingRandomClass: time: 328 milecond.

usingMathsRandom: time: 187 milekund.

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
źródło
1
W drugiej pętli sprawdzasz> = 50, co jest prawdą w ponad 50% przypadków. To spowoduje, że stwierdzenie to będzie prawdziwe w większości przypadków, co czyni je bardziej przewidywalnymi. Twoje wyniki są zatem tendencyjne na korzyść Twojej odpowiedzi
Neuron,
to pomyłka ... zrób 128 w drugim przykładzie otrzymasz ten sam wynik.
jatin Goyal