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

Клас Math у Java має спеціальний метод, який повертає випадкове число. І як ви вже, можливо, здогадуєтеся, цей метод має назву 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.