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


3509

Как создать случайное 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`.

Когда вам нужно много случайных чисел, я не рекомендую случайный класс в API. У него слишком маленький период. Попробуйте вместо этого твистер Мерсенна . Есть реализация Java .
Раупах

1
Прежде чем опубликовать новый ответ, учтите, что на этот вопрос уже есть более 65 ответов. Пожалуйста, убедитесь, что ваш ответ содержит информацию, которой нет среди существующих ответов.
Янник

Ответы:


3805

В 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 для выполнения задачи.


43
Для вызовов, где maxзначение является Integer.MAX_VALUEвозможным, возможно переполнение, приводящее к java.lang.IllegalArgumentException. Вы можете попробовать: randInt(0, Integer.MAX_VALUE). Кроме того, если nextInt((max-min) + 1)возвращает самое высокое значение (я полагаю, довольно редко), оно не переполнится снова (предположим, что min и max - достаточно высокие значения)? Как бороться с такими ситуациями?
Даниэль

3
@MoisheLipsker Должно быть, что nextLong не берет ограничение как nextInteger
ммм

13
@leventov ThreadLocalRandomбыл добавлен в Java через 2 с половиной года после того, как этот вопрос был впервые задан. Я всегда твердо придерживался мнения, что управление экземпляром Random находится за рамками вопроса.
Грег Кейс

6
В Android Random rand = new Random ();
Webserveis

5
@Webserveis Это рассматривается в комментариях к примеру. Короткая версия - вы не должны = new Random () для каждого вызова функции, иначе ваши результаты не будут достаточно случайными во многих случаях.
Грег Кейс

1422

Обратите внимание, что этот подход является более предвзятым и менее эффективным, чем 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), где «Макс-Мин» не включен.

Например, если вы хотите [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))

82
В документации Sun явно сказано, что вам лучше использовать Random (), если вам нужен int вместо Math.random (), который выдает double.
Лилиан А. Морару

6
Это на самом деле предвзято по сравнению с методами nextInt stackoverflow.com/a/738651/360211
weston

4
«Смещенный» в данном случае означает, что после 2 ^ 53 казней у некоторых чисел будет в среднем один дополнительный случай.
Головоногий

378

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

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

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



137

С они представили метод 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значения. Я надеюсь, что это помогает! :)


1
Я бы посоветовал вам создать экземпляр randomIterator только один раз. См. Грег Кейс комментирует его собственный ответ.
Naxos84

если вы можете, можете ли вы объяснить, в чем заключается значение streamSizeпервого приведенного параметра этого метода streamSize !=0? Какая разница, если streamSizeдано 1/2 / n?
Эрфан Ахмед

104

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

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

100

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

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

Смотрите больше здесь для реализации Random


Для минимума <= значение <максимум я сделал то же самое с Math: randomNum = минимум + (int) (Math.random () * (максимум-минимум)); но на кастинге не очень приятно смотреть;)
AxelH

Не менее 7 лет с опозданием дубликата ответа.
Мартен Бодевес

70

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

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

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


66

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


+1 для ссылки скриншота архива wayBackMachine, и ваш ответ по-прежнему полезен для получения «случайных» целых чисел в диапазоне.
Tapper7


@CodeConfident indexПеременная не повлияет на результат случайного числа. Вы можете инициализировать его любым удобным для вас способом, не беспокоясь об изменении результата. Надеюсь это поможет.
Мэтт Р

Точно ... это совершенно не используется. Я бы инициализировал его прямо к рэнду:int index = rand.nextInt(i.Length);
AjahnCharles

Или нет совсем. int index; \n index = rand...если увлекаюсь объявлениями и присваиваниями на разных строках. Некоторые стандарты кодирования являются более строгими (и без очевидной цели), чем другие.
Марк Сторер

49

Прости меня за привередливость, но решение, предложенное большинством, то есть 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% или выше.


Почему бы не создать исключение IllegalArgumentException, если разница = Integer.MAX_VALUE? Тогда вам не нужен цикл while.
Депутат Корстанье

3
@mpkorstanje Эта реализация предназначена для работы с любыми значениями min <= max, даже если их разность равна или даже больше, чем MAX_VALUE. Выполнение цикла до достижения успеха в этом случае является обычной моделью, гарантирующей равномерное распределение (если основной источник случайности является однородным). Random.nextInt (int) делает это внутренне, когда аргумент не является степенью 2.
Кристиан Семрау

44

Это можно сделать, просто выполнив утверждение:

Randomizer.generate(0,10); //min of zero, max of ten

Ниже его исходный код

Randomizer.java

public class Randomizer {
    public static int generate(int min,int max) {
        return min + (int)(Math.random() * ((max - min) + 1));
    }
}

Это просто чисто и просто.


5
Это могло быть редактированием другого примера, теперь это просто вопиющая копия репутации. Указание «ответа большинством голосов» также не очень прямое, оно может измениться.
Maarten Bodewes

1
Это верно @MaartenBodewes. Когда я написал этот ответ, ответ с большинством голосов все еще был написан в виде алгоритмического решения. Теперь решение выше сильно изменилось, и теперь этот ответ выглядел как подражатель.
Абель

Я действительно не понимаю, почему такие фундаментальные фрагменты кода не являются частью стандартных библиотек Java. Почему я должен это реализовать?
Leevi L


31

Давайте возьмем пример.

Предположим, я хочу сгенерировать число от 5 до 10 :

int max = 10;
int min = 5;
int diff = max - min;
Random rn = new Random();
int i = rn.nextInt(diff + 1);
i += min;
System.out.print("The Random Number is " + i);

Дайте нам понять это ...

Инициализируйте max с самым высоким значением и min с самым низким значением.

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

5, 6, 7, 8, 9, 10

Таким образом, счет этого будет макс - мин + 1.

то есть 10 - 5 + 1 = 6

Случайное число будет генерировать число от 0 до 5 .

т.е. 0, 1, 2, 3, 4, 5

Добавление минимального значения к случайному числу даст:

5, 6, 7, 8, 9, 10

Отсюда получаем желаемый диапазон.



26

Сгенерируйте случайное число для разницы min и max, используя метод nextint (n), а затем добавьте min число к результату:

Random rn = new Random();
int result = rn.nextInt(max - min + 1) + min;
System.out.println(result);

26

Я использую это:

 /**
   * @param min - The minimum.
   * @param max - The maximum.
   * @return A random double between these numbers (inclusive the minimum and maximum).
   */
 public static double getRandom(double min, double max) {
   return (Math.random() * (max + 1 - min)) + min;
 }

Вы можете привести его к Integer, если хотите.


Эта функция выдает одно и то же число снова и снова. В моем случае это было: 2147483647
сокрас

Fail: у вас есть функция, которая требует двойного, а затем выполнить + 1? Это, безусловно, идет вразрез с принципом наименьшего удивления. Что произойдет, если вы используете min = 0.1 и max = 0.2?
Maarten Bodewes

@sokras метод вызывает new Random(проверьте JavaDoc): «Создает новый генератор случайных чисел. Этот конструктор устанавливает начальное значение генератора случайных чисел на значение, которое, скорее всего, будет отличаться от любого другого вызова этого конструктора». Очень вероятно, может просто использовать текущее время в качестве семени. Если это время использует миллисекунды, то современные компьютеры достаточно быстры, чтобы генерировать одинаковое число. Но помимо этого 2147483647 есть Integer.MAX_VALUE; вывод, очевидно, зависит от ввода, который вы не указали.
Maarten Bodewes

Наконец нашел тот, который на самом деле работает
Дженси

23

Начиная с Java 7, вы больше не должны использовать Random. Для большинства случаев выбор генератора случайных чисел теперь ThreadLocalRandom.

Для пулов вилки и параллельных потоков используйте SplittableRandom.

Джошуа Блох. Эффективная Java. Третье издание.

Начиная с Java 8

Для пулов объединений и параллельных потоков использование SplittableRandom, как правило, более быстрое, обладает лучшей статистической независимостью и свойствами однородности по сравнению с Random.

Для генерации случайного числа intв диапазоне[0, 1_000]:

int n = new SplittableRandom().nextInt(0, 1_001);

Для генерации случайного int[100]массива значений в диапазоне[0, 1_000]:

int[] a = new SplittableRandom().ints(100, 0, 1_001).parallel().toArray();

Чтобы вернуть поток случайных значений:

IntStream stream = new SplittableRandom().ints(100, 0, 1_001);

Есть ли причина, почему пример включает в себя .parallel()? Мне кажется, что генерация 100 случайных чисел была бы слишком тривиальной, чтобы оправдать параллелизм.
Johnbot

@Johnbot Спасибо за комментарий, вы правы. Но основной причиной было показать API (конечно, разумный путь - это измерить производительность перед использованием parallelобработки). Кстати, для массива 1_000_000элементов parallelверсия на моей машине была в 2 раза быстрее по сравнению с последовательной.
Александр Пирохов

21

Просто используйте класс Random :

Random ran = new Random();
// Assumes max and min are non-negative.
int randomInt = min + ran.nextInt(max - min + 1);

8
Я не вижу здесь ничего нового, что не было опубликовано в бесчисленных предыдущих сообщениях.
Maarten Bodewes

20

Эти методы могут быть удобны в использовании:

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

public static int getRandomNumberBetween(int min, int max) {
    Random foo = new Random();
    int randomNumber = foo.nextInt(max - min) + min;
    if (randomNumber == min) {
        // Since the random number is between the min and max values, simply add 1
        return min + 1;
    } else {
        return randomNumber;
    }
}

и этот метод возвратит случайное число из предоставленного минимального и максимального значения (поэтому сгенерированное число также может быть минимальным или максимальным числом):

public static int getRandomNumberFrom(int min, int max) {
    Random foo = new Random();
    int randomNumber = foo.nextInt((max + 1) - min) + min;

    return randomNumber;
}

// Since the random number is between the min and max values, simply add 1, Почему? Мин не считается? Обычно диапазон составляет [min, max), где min включено, а max исключено. Неправильный ответ, проголосовал вниз.
Maarten Bodewes

@MaartenBodewes +1 добавлен, потому что getRandomNumberBetween генерирует случайное число, исключая предоставленные конечные точки.
Люк Тейлор

1
Число min + 1будет в два раза чаще, чем другое число, которое будет результатом getRandomNumberBetween!
Лий

19

В случае броска кубика это будет случайное число от 1 до 6 (не от 0 до 6), поэтому:

face = 1 + randomNumbers.nextInt(6);

19
int random = minimum + Double.valueOf(Math.random()*(maximum-minimum )).intValue();

Или взгляните на RandomUtils от Apache Commons .


Это полезно, но остерегайтесь небольшого недостатка: сигнатуры методов выглядят следующим образом: nextDouble (double startInclusive, double endInclusive), но если вы загляните внутрь методов, endInclusive на самом деле должно быть endExclusive.
Zakmck

Double.valueOf(Math.random()*(maximum-minimun)).intValue()это довольно запутанный (и неэффективный) способ сказать (int)(Math.random()*(maximum-minimun))
Хольгер

Несоответствие орфографии для минимального минимума возврата + Double.valueOf (Math.random () * (максимум - минимум)). IntValue ();
Ришикеш Мишра

18

Вот полезный класс для генерации случайных intsзначений в диапазоне с любой комбинацией включающих / исключающих границ:

import java.util.Random;

public class RandomRange extends Random {
    public int nextIncInc(int min, int max) {
        return nextInt(max - min + 1) + min;
    }

    public int nextExcInc(int min, int max) {
        return nextInt(max - min) + 1 + min;
    }

    public int nextExcExc(int min, int max) {
        return nextInt(max - min - 1) + 1 + min;
    }

    public int nextIncExc(int min, int max) {
        return nextInt(max - min) + min;
    }
}

18

Чтобы сгенерировать случайное число «между двумя числами», используйте следующий код:

Random r = new Random();
int lowerBound = 1;
int upperBound = 11;
int result = r.nextInt(upperBound-lowerBound) + lowerBound;

Это дает вам случайное число от 1 (включительно) до 11 (исключение), поэтому инициализируйте значение upperBound, добавив 1. Например, если вы хотите сгенерировать случайное число от 1 до 10, инициализируйте число upperBound с 11 вместо 10.


18

Вы можете достичь этого кратко в Java 8:

Random random = new Random();

int max = 10;
int min = 5;
int totalNumber = 10;

IntStream stream = random.ints(totalNumber, min, max);
stream.forEach(System.out::println);

17
public static Random RANDOM = new Random(System.nanoTime());

public static final float random(final float pMin, final float pMax) {
    return pMin + RANDOM.nextFloat() * (pMax - pMin);
}

16

Другой вариант - просто использовать Apache Commons :

import org.apache.commons.math.random.RandomData;
import org.apache.commons.math.random.RandomDataImpl;

public void method() {
    RandomData randomData = new RandomDataImpl();
    int number = randomData.nextInt(5, 10);
    // ...
 }

16

Я нашел этот пример Генерация случайных чисел :


Этот пример генерирует случайные целые числа в определенном диапазоне.

import java.util.Random;

/** Generate random integers in a certain range. */
public final class RandomRange {

  public static final void main(String... aArgs){
    log("Generating random integers in the range 1..10.");

    int START = 1;
    int END = 10;
    Random random = new Random();
    for (int idx = 1; idx <= 10; ++idx){
      showRandomInteger(START, END, random);
    }

    log("Done.");
  }

  private static void showRandomInteger(int aStart, int aEnd, Random aRandom){
    if ( aStart > aEnd ) {
      throw new IllegalArgumentException("Start cannot exceed End.");
    }
    //get the range, casting to long to avoid overflow problems
    long range = (long)aEnd - (long)aStart + 1;
    // compute a fraction of the range, 0 <= frac < range
    long fraction = (long)(range * aRandom.nextDouble());
    int randomNumber =  (int)(fraction + aStart);    
    log("Generated : " + randomNumber);
  }

  private static void log(String aMessage){
    System.out.println(aMessage);
  }
} 

Пример запуска этого класса:

Generating random integers in the range 1..10.
Generated : 9
Generated : 3
Generated : 3
Generated : 9
Generated : 4
Generated : 1
Generated : 3
Generated : 9
Generated : 10
Generated : 10
Done.

14

Лучше использовать SecureRandom , чем просто Random.

public static int generateRandomInteger(int min, int max) {
    SecureRandom rand = new SecureRandom();
    rand.setSeed(new Date().getTime());
    int randomNum = rand.nextInt((max - min) + 1) + min;
    return randomNum;
}

1
Это не очень хорошо, потому что если он выполняется за одну и ту же миллисекунду, вы получите тот же номер, вам нужно поместить инициализацию rand и setSeet вне метода.
Марака

Вам нужен seed, да, но с использованием SecureRandom.
grep

Извините, но тот, кто отклонил запрос на изменение, не имеет ни малейшего представления о программировании на Java. Это хорошее предложение, но оно ошибочно, поскольку при выполнении за одну и ту же миллисекунду оно даст одинаковое число, а не случайное.
Марака

Правильное решение нигде не найти, private static int SecureRandom rand = new SecureRandom();static {rand.setSeed(...);}
немногие

5
Нет нужды сеять SecureRandom, это будет посеяно системой. Прямой вызов setSeedочень опасен, он может заменить (действительно случайное) семя датой. И это, конечно же, не приведет к тому SecureRandom, что любой может угадать время и попытаться заснять свой SecureRandomэкземпляр этой информацией.
Maarten Bodewes

13

Вот простой пример, который показывает, как генерировать случайное число из закрытого [min, max]диапазона, в то время какmin <= max is true

Вы можете использовать его как поле в классе отверстий, также имея все Random.classметоды в одном месте

Пример результатов:

RandomUtils random = new RandomUtils();
random.nextInt(0, 0); // returns 0
random.nextInt(10, 10); // returns 10
random.nextInt(-10, 10); // returns numbers from -10 to 10 (-10, -9....9, 10)
random.nextInt(10, -10); // throws assert

Источники:

import junit.framework.Assert;
import java.util.Random;

public class RandomUtils extends Random {

    /**
     * @param min generated value. Can't be > then max
     * @param max generated value
     * @return values in closed range [min, max].
     */
    public int nextInt(int min, int max) {
        Assert.assertFalse("min can't be > then max; values:[" + min + ", " + max + "]", min > max);
        if (min == max) {
            return max;
        }

        return nextInt(max - min + 1) + min;
    }
}

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.