Генериране на случайни числа в Java

1. Общ преглед

В този урок ще изследваме различни начини за генериране на случайни числа в Java.

2. Използване на Java API

API на Java ни предоставя няколко начина да постигнем целта си. Нека да видим някои от тях.

2.1. java.lang.Math

На случаен метода на Math класа ще върне двойно стойност в диапазона от 0,0 (включително) до 1.0 (изключително). Нека видим как бихме го използвали, за да получим произволно число в даден диапазон, определен от min и max :

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

2.2. java.util.Random

Преди 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 и целочислени методи. С nextInt можем да зададем директно горния и долния диапазон, като използваме извикването на двата параметъра:

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

Този начин на използване проверява дали параметърът max е по-голям от min . В противен случай ще получим 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;

We must remember that this way of using it throws IllegalArgumentException if the parameter is not bigger than zero.

3. Using Third-Party APIs

As we have seen, Java provides us with a lot of classes and methods for generating random numbers. However, there are also third-party APIs for this purpose.

We're going to take a look at some of them.

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

There are a lot of generators in the commons mathematics library from the Apache Commons project. The easiest, and probably the most useful, is the RandomDataGenerator. It uses the Well19937c algorithm for the random generation. However, we can provide our algorithm implementation.

Let’s see how to use it. Firstly, we have to add dependency:

 org.apache.commons commons-math3 3.6.1 

The latest version of commons-math3 can be found on Maven Central.

Then we can start working with it:

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

3.2. it.unimi.dsi.util.XoRoShiRo128PlusRandom

Certainly, this is one of the fastest random number generator implementations. It has been developed at the Information Sciences Department of the Milan University.

The library is also available at Maven Central repositories. So, let's add the dependency:

 it.unimi.dsi dsiutils 2.6.0 

Този генератор наследява от java.util.Random . Ако обаче погледнем JavaDoc, осъзнаваме, че има само един начин за използването му - чрез метода nextInt . Преди всичко, този метод е достъпен само с извиквания с нула и един параметър. Всяко от другите извиквания ще използва директно методите java.util.Random .

Например, ако искаме да получим произволно число в диапазон, ще напишем:

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

4. Заключение

Има няколко начина за реализиране на генериране на произволни числа. Няма обаче най-добрия начин. Следователно трябва да изберем този, който най-добре отговаря на нашите нужди.

Пълният пример може да бъде намерен в GitHub.