1. Псевдослучайные числа

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

Вообще-то, если вам нужно получить «настоящее случайное число», сделать это довольно-таки трудно. Вплоть до того, что в компьютер встраивают специальные математические сопроцессоры, которые умеют генерировать такие числа, с выполнением всех требований к «истинной случайности».

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

Есть много алгоритмов генерации последовательности псевдослучайных чисел и почти все из них генерируют следующее случайное число на основе предыдущего и еще каких-то вспомогательных чисел.

Например, данная программа выведет на экран 1000 неповторяющихся чисел:

public class Main
{
   public static int a = 41;
   public static int c = 11119;
   public static int m = 11113;
   public static int seed = 1;

   public static int getNextRandom()
   {
     seed = (a * seed + c) % m;
     return seed;
   }

   public static void main(String[] args)
   {
     for (int i = 0; i < 1000; i++)
     {
       System.out.println(getNextRandom());
     }
   }
}

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

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

public static int getRandomNumber()
{
   return 4; // это точно случайное число (выкинул его с помощью кубиков)
}

2. Метод Math.random()

В Java у класса Math есть специальный метод, который возвращает случайное число. И, как вы возможно догадываетесь, метод называется random. Общий вид его вызова выглядит так:

Math.random()

Этот метод не принимает никаких параметров, но возвращает результат — псевдослучайное вещественное число в диапазоне от 0 до 1. Единица при этом в диапазон не входит.

Пример:

Код Вывод на экран
public class Main
{
   public static void main(String[] args)
   {
     for (int i = 0; i < 10; i++)
     {
       System.out.println(Math.random());
     }
   }
} 
0.9703753971734451
0.09979423801773157
0.994048474709053
0.2852203204171295
0.13551248551226025
0.3128547131272822
0.5342480554101412
0.6817369932044817
0.1840767788961758
0.06969563435451254

Но что, если вам этот метод не очень подходит, а вы хотите, допустим, написать программу, которая имитирует выбрасывание кубика с шестью гранями. Как получить случайные целые числа в диапазоне 1..6, а не вещественные в диапазоне 0..1?

Это на самом деле довольно просто.

Сначала нужно превратить диапазон [0,1) в [0, 6). Для этого нужно просто умножить результат функции random() на 6. Ну а чтобы получить целые числа, нужно это все округлить:

Код Вывод на экран
public class Main
{
   public static int getRandomDiceNumber()
   {
      return (int) (Math.random() * 6);
   }

   public static void main(String[] args)
   {
      for (int i = 0; i < 10; i++)
      {
         int x = getRandomDiceNumber();
         System.out.println(x);
      }
   }
}
5
2
3
3
2
4
1
1
5
0

Функция getRandomDiceNumber() возвращает случайное целое число из диапазона 0..5 включительно. Только это будут не числа из набора 1,2,3,4,5,6, а числа из набора 0,1,2,3,4,5.

Если требуются именно числа из набора 1,2,3,4,5,6, нужно просто ко всем случайным числам добавлять единицу:

Код Вывод на экран
public class Main
{
   public static int getRandomDiceNumber()
   {
      return (int) (Math.random() * 6) + 1;
   }

   public static void main(String[] args)
   {
     for (int i = 0; i < 10; i++)
     {
       int x = getRandomDiceNumber();
       System.out.println(x);
     }
   }
}
3
2
1
3
6
5
6
1
6
6

Вот теперь идеально!



3. Класс Random

В Java есть специальный класс Random, который инкапсулирует в себе последовательность псевдослучайных чисел. Можно создать несколько объектов класса Random, и каждый из этих объектов будет генерировать свою последовательность псевдослучайных чисел.

Это очень интересный класс, и у него есть много интересных методов. Начнем с самых простых:

Метод double nextDouble()

Этот метод возвращает случайное вещественное число в диапазоне 0.01.0. Очень похоже на метод Math.random(). И ничего удивительного, ведь метод Math.random() просто вызывает метод nextDouble() у объекта типа Random.

Метод float nextFloat()

Метод очень похож на метод nextDouble(), только возвращаемое случайное число типа float. Оно также лежит в диапазоне 0.01.0. И, как всегда, в Java диапазон не включает число 1.0.

Random r = new Random();
float f = r.nextFloat();

Метод int nextInt(int max)

Этот метод возвращает случайное целое число в диапазоне [0, max). 0 входит в диапазон, max — не входит.

Т.е. если вы хотите получить случайное число из набора 1, 2, 3, 4, 5, 6, вам нужно будет прибавить к полученному случайному числу единицу:

Random r = new Random();
int x = r.nextInt(6) + 1;

Метод int nextInt()

Этот метод аналогичен предыдущему, но не принимает никаких параметров. Тогда в каком же диапазоне он выдает числа? От -2 миллиарда до +2 миллиарда.

Ну или если точнее, от -2147483648 до +2147483647.

Метод long nextLong()

Этот метод аналогичен методу nextInt(), только возвращаемое значение будет из всего возможного диапазона значений типа long.

Метод boolean nextBoolean()

Этот метод возвращает случайное значение типа boolean: false или true. Очень удобно, если нужно получить длинную последовательность случайных логических значений.

Метод void nextBytes(byte[] data)

Этот метод ничего не возвращает (тип void). Вместо этого он заполняет переданный в него массив случайными значениями. Очень удобно, если нужен большой буфер, заполненный случайными данными.

Метод double nextGaussian()

Этот метод возвращает случайное вещественное число в диапазоне 0.01.0. Вот только числа в этом диапазоне распределены не равномерно, а подчиняются нормальному распределению.

Числа ближе к середине диапазона (0.5) будут выпадать чаще, чем значение по краям диапазона.

Класс Random

Пик значений в нашем случае придется на 0.5