Перейти к основному содержимому

Генерация случайных чисел в Java

· 6 мин. чтения

1. Обзор

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

2. Использование Java-API

Java API предоставляет нам несколько способов достижения нашей цели. Давайте посмотрим на некоторые из них.

2.1. java.lang.Math

Случайный метод класса Math вернет двойное значение в диапазоне от 0,0 (включительно) до 1,0 (не включая). Давайте посмотрим, как мы будем использовать его для получения случайного числа в заданном диапазоне, определяемом min и max :

int randomWithMathRandom = (int) ((Math.random() * (max - min)) + min);

2.2. java.util. Случайный

До Java 1.7 самым популярным способом генерации случайных чисел было использование nextInt . Было два способа использования этого метода, с параметрами и без них. Вызов без параметров возвращает любое из значений int с примерно равной вероятностью. Итак, очень вероятно, что мы получим отрицательные числа:

Random random = new Random();
int randomWithNextInt = random.nextInt();

Если мы используем вызов netxInt с привязанным параметром, мы получим числа в пределах диапазона:

int randomWintNextIntWithinARange = random.nextInt(max - min) + min;

Это даст нам число от 0 (включительно) до параметра (исключительно). Таким образом, связанный параметр должен быть больше 0. В противном случае мы получим исключение java.lang.IllegalArgumentException .

В Java 8 появились новые методы ints , которые возвращают java.util.stream.IntStream. Давайте посмотрим, как их использовать.

Метод ints без параметров возвращает неограниченный поток значений int :

IntStream unlimitedIntStream = random.ints();

Мы также можем передать один параметр, чтобы ограничить размер потока:

IntStream limitedIntStream = random.ints(streamSize);

И, конечно же, мы можем установить максимум и минимум для генерируемого диапазона:

IntStream limitedIntStreamWithinARange = random.ints(streamSize, min, max);

2.3. java.util.concurrent.ThreadLocalRandom

В версии Java 1.7 появился новый и более эффективный способ генерации случайных чисел с помощью класса ThreadLocalRandom . Он имеет три важных отличия от класса Random :

  • Нам не нужно явно инициировать новый экземпляр ThreadLocalRandom . Это помогает нам избежать ошибок, связанных с созданием множества бесполезных экземпляров и тратой времени на сборщик мусора.
  • Мы не можем установить начальное значение для ThreadLocalRandom , что может привести к реальной проблеме. Если нам нужно установить начальное число, нам следует избегать этого способа генерации случайных чисел.
  • Случайный класс плохо работает в многопоточных средах

Теперь давайте посмотрим, как это работает:

int randomWithThreadLocalRandomInARange = ThreadLocalRandom.current().nextInt(min, max);

С Java 8 или выше у нас появились новые возможности. Во-первых, у нас есть два варианта метода nextInt :

int randomWithThreadLocalRandom = ThreadLocalRandom.current().nextInt();
int randomWithThreadLocalRandomFromZero = ThreadLocalRandom.current().nextInt(max);

Во-вторых, что более важно, мы можем использовать метод ints :

IntStream streamWithThreadLocalRandom = ThreadLocalRandom.current().ints();

2.4. java.util.SplittableRandom

В Java 8 также появился очень быстрый генератор — класс SplittableRandom .

Как видно из JavaDoc, это генератор для использования в параллельных вычислениях. Важно знать, что экземпляры не потокобезопасны. Итак, мы должны соблюдать осторожность при использовании этого класса.

У нас есть методы nextInt и ints . С помощью nextInt мы можем напрямую установить верхний и нижний диапазон, используя вызов двух параметров:

SplittableRandom splittableRandom = new SplittableRandom();
int randomWithSplittableRandom = splittableRandom.nextInt(min, max);

Этот способ использования проверяет, что максимальный параметр больше минимального . В противном случае мы получим исключение IllegalArgumentException . Однако он не проверяет, работаем ли мы с положительными или отрицательными числами. Таким образом, любой из параметров может быть отрицательным. Кроме того, у нас есть доступные вызовы с одним и нулевым параметром. Они работают так же, как мы описали ранее.

У нас также есть методы ints . Это означает, что мы можем легко получить поток значений int . Чтобы уточнить, мы можем выбрать ограниченный или неограниченный поток. Для ограниченного потока мы можем установить верх и низ для диапазона генерации чисел:

IntStream limitedIntStreamWithinARangeWithSplittableRandom = splittableRandom.ints(streamSize, min, max);

2.5. java.security.SecureRandom

Если у нас есть приложения, чувствительные к безопасности, мы должны рассмотреть возможность использования SecureRandom . Это криптографически стойкий генератор. Экземпляры, созданные по умолчанию, не используют криптографически случайные начальные значения. Итак, мы должны либо:

  • Установить сид — следовательно, сид будет непредсказуемым
  • Установите для системного свойства java.util.secureRandomSeed значение true .

Этот класс наследуется от java.util.Random . Итак, нам доступны все способы, которые мы видели выше. Например, если нам нужно получить любое из значений int , то вызовем nextInt без параметров:

SecureRandom secureRandom = new SecureRandom();
int randomWithSecureRandom = secureRandom.nextInt();

С другой стороны, если нам нужно установить диапазон, мы можем вызвать его с привязанным параметром:

int randomWithSecureRandomWithinARange = secureRandom.nextInt(max - min) + min;

Мы должны помнить, что этот способ использования генерирует исключение IllegalArgumentException , если параметр не больше нуля.

3. Использование сторонних API

Как мы видели, Java предоставляет нам множество классов и методов для генерации случайных чисел. Однако для этой цели также существуют сторонние API.

Мы собираемся взглянуть на некоторые из них.

3.1. org.apache.commons.math3.random.RandomDataGenerator

В математической библиотеке Commons из проекта Apache Commons есть много генераторов. Самый простой и, вероятно, самый полезный — RandomDataGenerator . Он использует алгоритм Well19937c для случайной генерации. Однако мы можем предоставить реализацию нашего алгоритма.

Давайте посмотрим, как его использовать. Во-первых, мы должны добавить зависимость:

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-math3</artifactId>
<version>3.6.1</version>
</dependency>

Последнюю версию commons-math3 можно найти на Maven Central .

Затем мы можем начать работать с ним:

RandomDataGenerator randomDataGenerator = new RandomDataGenerator();
int randomWithRandomDataGenerator = randomDataGenerator.nextInt(min, max);

3.2. it.unimi.dsi.util.XoRoShiRo128PlusСлучайный

Безусловно, это одна из самых быстрых реализаций генератора случайных чисел. Он был разработан на факультете информационных наук Миланского университета.

Библиотека также доступна в репозиториях Maven Central . Итак, добавим зависимость:

<dependency>
<groupId>it.unimi.dsi</groupId>
<artifactId>dsiutils</artifactId>
<version>2.6.0</version>
</dependency>

Этот генератор наследуется от java.util.Random . Однако, если мы посмотрим на JavaDoc , мы поймем, что есть только один способ его использования — через метод nextInt . Прежде всего, этот метод доступен только для вызовов с нулевым и одним параметром. Любые другие вызовы будут напрямую использовать методы java.util.Random .

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

XoRoShiRo128PlusRandom xoroRandom = new XoRoShiRo128PlusRandom();
int randomWithXoRoShiRo128PlusRandom = xoroRandom.nextInt(max - min) + min;

4. Вывод

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

Полный пример можно найти на GitHub .