Pytanie Jak generować losowe liczby całkowite w określonym zakresie w Javie?


Jak wygenerować losowy int wartość w określonym zakresie?

Próbowałem następujące, ale te nie działają:

Próba 1:

randomNum = minimum + (int)(Math.random() * maximum);
// Bug: `randomNum` can be bigger than `maximum`.

Próba 2:

Random rn = new Random();
int n = maximum - minimum + 1;
int i = rn.nextInt() % n;
randomNum =  minimum + i;
// Bug: `randomNum` can be smaller than `minimum`.

2903
2017-12-12 18:20


pochodzenie




Odpowiedzi:


W Java 1.7 lub nowsza, standardowy sposób to zrobić jest następujący:

import java.util.concurrent.ThreadLocalRandom;

// nextInt is normally exclusive of the top value,
// so add 1 to make it inclusive
int randomNum = ThreadLocalRandom.current().nextInt(min, max + 1);

Widzieć odpowiednią JavaDoc. Takie podejście ma tę zaletę, że nie wymaga jawnego inicjowania java.util.Random instancja, która może być źródłem pomyłki i błędu, jeśli zostanie użyta niewłaściwie.

Jednak na odwrót nie ma możliwości wyraźnego ustawienia materiału siewnego, co może być trudne do odtworzenia wyników w sytuacjach, w których jest to użyteczne, na przykład testowanie lub zapisywanie stanów gry lub podobnych. W takich sytuacjach można użyć przedstawionej poniżej techniki pre-Java 1.7.

Przed wersją Java 1.7, standardowy sposób to zrobić jest następujący:

import java.util.Random;

/**
 * Returns a pseudo-random number between min and max, inclusive.
 * The difference between min and max can be at most
 * <code>Integer.MAX_VALUE - 1</code>.
 *
 * @param min Minimum value
 * @param max Maximum value.  Must be greater than min.
 * @return Integer between min and max, inclusive.
 * @see java.util.Random#nextInt(int)
 */
public static int randInt(int min, int max) {

    // NOTE: This will (intentionally) not run as written so that folks
    // copy-pasting have to think about how to initialize their
    // Random instance.  Initialization of the Random instance is outside
    // the main scope of the question, but some decent options are to have
    // a field that is initialized once and then re-used as needed or to
    // use ThreadLocalRandom (if using at least Java 1.7).
    // 
    // In particular, do NOT do 'Random rand = new Random()' here or you
    // will get not very good / not very random results.
    Random rand;

    // nextInt is normally exclusive of the top value,
    // so add 1 to make it inclusive
    int randomNum = rand.nextInt((max - min) + 1) + min;

    return randomNum;
}

Widzieć odpowiednią JavaDoc. W praktyce java.util.Random klasa jest często preferowana java.lang.Math.random ().

W szczególności nie ma potrzeby ponownego odkrywania przypadkowego koła generującego liczbę całkowitą, jeśli istnieje prosty interfejs API w standardowej bibliotece, aby wykonać zadanie.


3260
2017-12-12 18:25



Dla połączeń, gdzie max wartosc jest Integer.MAX_VALUE możliwe jest przepełnienie, w wyniku czego powstaje a java.lang.IllegalArgumentException. Możesz spróbować z: randInt(0, Integer.MAX_VALUE). Także jeśli nextInt((max-min) + 1) zwraca najwyższą wartość (dość rzadko, jak zakładam), czy nie przepełni się ponownie (jeśli min i max są wystarczająco wysokie)? Jak radzić sobie z tego rodzaju sytuacjami? - Daniel
To nie działa dla długich - momo
@MoisheLipsker Musi być tak, że nextLong nie przyjmuje powiązania jako nextInteger - momo
@leventov ThreadLocalRandom został dodany do środowiska Java 2 1/2 roku po tym, jak zadano mu to pytanie. Zawsze byłem przekonany, że zarządzanie przypadkiem losowym wykracza poza zakres pytania. - Greg Case
W Android Random rand = new Random (); - Webserveis


Należy zauważyć, że to podejście jest bardziej stronnicze i mniej wydajne niż nextInt podejście, https://stackoverflow.com/a/738651/360211

Jednym ze standardowych wzorców do osiągnięcia tego jest:

Min + (int)(Math.random() * ((Max - Min) + 1))

The Jawa Funkcja biblioteki matematycznej Math.random () generuje podwójną wartość w zakresie [0,1). Zauważ, że ten zakres nie obejmuje 1.

Aby najpierw uzyskać określony zakres wartości, należy pomnożyć przez wielkość zakresu wartości, które chcesz pokryć.

Math.random() * ( Max - Min )

Zwraca wartość w zakresie [0,Max-Min), gdzie "Max-Min" nie jest wliczony w cenę.

Na przykład, jeśli chcesz [5,10), musisz pokryć pięć liczb całkowitych, których używasz

Math.random() * 5

To zwróci wartość w zakresie [0,5), gdzie 5 nie jest uwzględnione.

Teraz musisz przesunąć ten zakres do zasięgu, na który kierujesz. Robisz to, dodając wartość Min.

Min + (Math.random() * (Max - Min))

Otrzymasz teraz wartość w zakresie [Min,Max). Podążając za naszym przykładem, oznacza to [5,10):

5 + (Math.random() * (10 - 5))

Ale to nadal nie obejmuje Max i dostajesz podwójną wartość. Aby uzyskać Max wartość wliczona, musisz dodać 1 do swojego parametru zasięgu (Max - Min) a następnie obcina część dziesiętną przez rzutowanie na int. Osiąga się to poprzez:

Min + (int)(Math.random() * ((Max - Min) + 1))

I tam to masz. Losowa liczba całkowita w zakresie [Min,Max]lub według przykładu [5,10]:

5 + (int)(Math.random() * ((10 - 5) + 1))

1324
2017-12-12 18:35



Dokumentacja Sun wyraźnie mówi, że powinieneś lepiej używać Random (), jeśli potrzebujesz int zamiast Math.random (), który produkuje podwójne. - Lilian A. Moraru
Jest to faktycznie stronnicze w porównaniu do następnych metod stackoverflow.com/a/738651/360211 - weston


Posługiwać się:

Random ran = new Random();
int x = ran.nextInt(6) + 5;

Liczba całkowita x jest teraz liczbą losową, która ma możliwy wynik 5-10.


311
2017-09-04 04:23





Posługiwać się:

minimum + rn.nextInt(maxValue - minvalue + 1)

122
2017-12-12 18:25





Z  wprowadzili tę metodę ints(int randomNumberOrigin, int randomNumberBound) w Random klasa.

Na przykład, jeśli chcesz wygenerować pięć losowych liczb całkowitych (lub jednego) w zakresie [0, 10], po prostu wykonaj:

Random r = new Random();
int[] fiveRandomNumbers = r.ints(5, 0, 11).toArray();
int randomNumber = r.ints(1, 0, 11).findFirst().getAsInt();

Pierwszy parametr wskazuje tylko rozmiar pliku IntStream wygenerowany (który jest przeciążoną metodą tego, który wytwarza nieograniczoną liczbę) IntStream).

Jeśli potrzebujesz wykonać wiele oddzielnych połączeń, możesz utworzyć nieskończony prymitywny iterator ze strumienia:

public final class IntRandomNumberGenerator {

    private PrimitiveIterator.OfInt randomIterator;

    /**
     * Initialize a new random number generator that generates
     * random numbers in the range [min, max]
     * @param min - the min value (inclusive)
     * @param max - the max value (inclusive)
     */
    public IntRandomNumberGenerator(int min, int max) {
        randomIterator = new Random().ints(min, max + 1).iterator();
    }

    /**
     * Returns a random number in the range (min, max)
     * @return a random number in the range (min, max)
     */
    public int nextInt() {
        return randomIterator.nextInt();
    }
}

Możesz to również zrobić double i long wartości.

Mam nadzieję, że to pomoże! :)


99
2017-11-26 18:29



Sugerowałbym, aby instancja randomIterator była uruchamiana tylko raz. Zobacz komentarz Grega Case'a na temat własnej odpowiedzi. - Naxos84
jeśli możesz, możesz wyjaśnić, jakie jest znaczenie streamSize - podany pierwszy parametr tej metody streamSize !=0. Jaka jest różnica, jeśli streamSize 1/2 / n jest podana? - Erfan Ahmed Emon
Słodkie rozwiązanie, szczególnie klasa strumienia. - cen


Możesz edytować swój drugi przykład kodu, aby:

Random rn = new Random();
int range = maximum - minimum + 1;
int randomNum =  rn.nextInt(range) + minimum;

90
2017-12-12 18:31





Wystarczyłaby drobna modyfikacja twojego pierwszego rozwiązania.

Random rand = new Random();
randomNum = minimum + rand.nextInt((maximum - minimum) + 1);

Zobacz więcej tutaj na temat implementacji Random


89
2018-03-12 22:44



Dla minimalnej <= wartości <maksimum, zrobiłem to samo z Math: randomNum = minimum + (int) (Math.random () * (maksimum-minimum)); ale rzucanie nie jest zbyt miłe do zobaczenia;) - AxelH


Odpowiednik klasy ThreadLocalRandom klasy java.util.Random dla środowiska wielowątkowego. Generowanie liczby losowej odbywa się lokalnie w każdym wątku. Mamy więc lepszą wydajność, redukując konflikty.

int rand = ThreadLocalRandom.current().nextInt(x,y);

x, y - interwały np. (1,10)


54
2018-02-12 23:19



Na Boga, ta odpowiedź powinna zostać przyjęta. Zobacz też Czy jest jakikolwiek powód do napisania? new Random() od wersji Java 8? - leventov
@leventov to nie może być zaakceptowana odpowiedź, ta implementacja obsługuje tylko => LOLLIPOP - Relm
Ludzie powinni teraz rozwijać> Lollipop, jeśli tego nie zrobi, po prostu przeciąga rynek. Spraw, by Twoje aplikacje obsługiwały nowsze interfejsy API, aby wymusić aktualizację przez operatorów. - Martin Marconcini