Как генерировать случайные целые числа в определенном диапазоне в Java? - PullRequest
3225 голосов
/ 12 декабря 2008

Как создать случайное значение int в определенном диапазоне?

Я пробовал следующее, но они не работают:

Попытка 1:

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

Попытка 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`.

Ответы [ 64 ]

3575 голосов
/ 12 декабря 2008

В Java 1.7 или более поздней версии , стандартный способ сделать это следующим образом:

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);

См. соответствующий JavaDoc . Преимущество этого подхода заключается в том, что нет необходимости явно инициализировать экземпляр java.util.Random , который может стать источником путаницы и ошибок при неправильном использовании.

Однако, наоборот, нет способа явно установить начальное число, поэтому может быть трудно воспроизвести результаты в ситуациях, когда это полезно, таких как тестирование или сохранение состояний игры или аналогичных. В этих ситуациях может использоваться методика до Java 1.7, показанная ниже.

До Java 1.7 стандартный способ сделать это выглядит следующим образом:

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;
}

См. соответствующий JavaDoc . На практике класс java.util.Random часто предпочтительнее, чем java.lang.Math.random () .

В частности, нет необходимости заново изобретать колесо генерации случайных целых чисел, когда в стандартной библиотеке имеется простой API для выполнения задачи.

1381 голосов
/ 12 декабря 2008

Обратите внимание, что этот подход более предвзятый и менее эффективный, чем подход nextInt, https://stackoverflow.com/a/738651/360211

Один стандартный шаблон для достижения этой цели:

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

Библиотечная функция Java Math Math.random () генерирует двойное значение в диапазоне [0,1). Обратите внимание, что этот диапазон не включает 1.

Чтобы сначала получить определенный диапазон значений, необходимо умножить его на величину диапазона значений, который вы хотите охватить.

Math.random() * ( Max - Min )

Возвращает значение в диапазоне [0,Max-Min), где «Max-Min» не включено.

Например, если вы хотите [5,10), вам нужно охватить пять целочисленных значений, чтобы вы использовали

Math.random() * 5

Возвращает значение в диапазоне [0,5), где 5 не включено.

Теперь вам нужно сместить этот диапазон до диапазона, на который вы нацеливаетесь. Вы делаете это, добавляя значение Min.

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

Теперь вы получите значение в диапазоне [Min,Max). Следуя нашему примеру, это означает [5,10):

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

Но это по-прежнему не включает Max, и вы получаете двойное значение. Чтобы получить включенное значение Max, вам нужно добавить 1 к параметру диапазона (Max - Min) и затем усечь десятичную часть, приведя к int. Это достигается с помощью:

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

И вот оно у вас. Случайное целочисленное значение в диапазоне [Min,Max] или по примеру [5,10]:

5 + (int)(Math.random() * ((10 - 5) + 1))
338 голосов
/ 04 сентября 2009

Использование:

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

Целое число x теперь является случайным числом с возможным исходом 5-10.

143 голосов
/ 12 декабря 2008

Использование:

minimum + rn.nextInt(maxValue - minvalue + 1)
117 голосов
/ 26 ноября 2014

С они ввели метод ints(int randomNumberOrigin, int randomNumberBound) в классе Random.

Например, если вы хотите сгенерировать пять случайных целых (или одно) в диапазоне [0, 10], просто выполните:

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

Первый параметр указывает только размер сгенерированного IntStream (который является перегруженным методом того, который генерирует неограниченный IntStream).

Если вам нужно сделать несколько отдельных вызовов, вы можете создать бесконечный примитивный итератор из потока:

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();
    }
}

Вы также можете сделать это для значений double и long.

Надеюсь, это поможет! :)

100 голосов
/ 12 декабря 2008

Вы можете отредактировать ваш второй пример кода:

Random rn = new Random();
int range = maximum - minimum + 1;
int randomNum =  rn.nextInt(range) + minimum;
97 голосов
/ 13 марта 2015

Достаточно небольшой модификации вашего первого решения.

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

Подробнее о реализации Random

см. Здесь
62 голосов
/ 13 февраля 2013

ThreadLocalRandom эквивалент класса java.util.Random для многопоточной среды. Генерация случайного числа осуществляется локально в каждом из потоков. Таким образом, мы имеем лучшую производительность за счет уменьшения конфликтов.

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

x, y - интервалы, например (1,10)

61 голосов
/ 08 января 2009

Класс Math.Random в Java основан на 0. Итак, если вы напишите что-то вроде этого:

Random rand = new Random();
int x = rand.nextInt(10);

x будет между 0-9 включительно.

Итак, учитывая следующий массив 25 элементов, код для генерации случайного числа между 0 (основание массива) и array.length будет:

String[] i = new String[25];
Random rand = new Random();
int index = 0;

index = rand.nextInt( i.length );

Поскольку i.length вернет 25, nextInt( i.length ) вернет число в диапазоне 0-24. Другая опция работает с Math.Random, который работает таким же образом.

index = (int) Math.floor(Math.random() * i.length);

Для лучшего понимания прочитайте сообщение на форуме Случайные интервалы (archive.org) .

48 голосов
/ 10 января 2011

Простите, что я привередлив, но решение, предложенное большинством, то есть min + rng.nextInt(max - min + 1)), кажется опасным из-за того, что:

  • rng.nextInt(n) не может достичь Integer.MAX_VALUE.
  • (max - min) может вызвать переполнение, если min отрицательно.

Надежное решение вернет правильные результаты для любого min <= max в пределах [Integer.MIN_VALUE, Integer.MAX_VALUE]. Рассмотрим следующую наивную реализацию:

int nextIntInRange(int min, int max, Random rng) {
   if (min > max) {
      throw new IllegalArgumentException("Cannot draw random int from invalid range [" + min + ", " + max + "].");
   }
   int diff = max - min;
   if (diff >= 0 && diff != Integer.MAX_VALUE) {
      return (min + rng.nextInt(diff + 1));
   }
   int i;
   do {
      i = rng.nextInt();
   } while (i < min || i > max);
   return i;
}

Хотя это неэффективно, обратите внимание, что вероятность успеха в цикле while всегда будет 50% или выше.

...