Случайные числа – это важная часть многих программ и игр. Они придают приложениям элемент неопределённости и интереса. Однако, на самом деле, компьютеры не способны генерировать абсолютно случайные числа. Вместо этого они используют псевдослучайные числа, которые создаются с использованием некоторых алгоритмов. Как сгенерировать случайное число в Java? Обо всём по порядку.
Содержание:
Псевдослучайность
Псевдослучайные числа – это числа, которые создаются на основе начального значения, называемого “сид” (seed). Если вы используете один и тот же сид, то вы всегда получите одну и ту же последовательность чисел. Это может быть полезно для отладки и воспроизведения результатов, но не является истинно случайным.
Для создания статистически непредсказуемых чисел используются различные сложные формулы. У каждого такого алгоритма должен быть достаточно длинный период. Любой генератор рано или поздно начинает повторять последовательность, и чем позже это случится, тем лучше.
Но мы не будем писать такие генераторы – а зачем? Уже есть готовые реализации рандома, и в Java это встроенные библиотеки. Познакомимся с ними.
java.util.Random
В Java есть достаточно удобный класс для операций со случайными числами, и это – класс Random из пакета java.util. Попробуем поиграться с его функционалом:
1 2 3 4 5 6 7 8 9 10 11 12 |
import java.util.Random; public class MyProject { public static void main(String[] args) { Random random = new Random(); System.out.println("Случайное целое число: " + random.nextInt()); System.out.println("Случайное целое число в диапазоне [0; 10): " + random.nextInt(10)); System.out.println("Случайное вещественное число: " + random.nextDouble()); System.out.println("Случайный boolean: " + random.nextBoolean()); } } |
Отличный результат. И значения при перезапуске программы будут меняться – на то они и (псевдо)случайные. Хотя у класса Random есть метод генерации целого числа int до определённой верхней границы, нижнюю границу мы задать всё-таки не можем. Это можно исправить.
Давайте напишем класс, который позволит генерировать случайные целые и вещественные (дробные) числа в задаваемых диапазонах. Кстати, random.nextDouble() генерирует вещественное число весьма своеобразным образом – от 0 до 1 (единица не включена в диапазон). Перед классом разместим javadoc-комментарий – один из трёх видов комментариев в Java.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
import java.util.Random; /** * класс для удобной генерации (псевдо)случайных чисел * * @author Катя Левкович * @version 1.0, 07.09.2023 */ public class Randomizer { private Random random; public Randomizer() { random = new Random(); } /** * генерация случайного целого числа int в заданном диапазоне * с включением min и max в диапазон */ public int generateInt(int min, int max) { int variationCount = max - min + 1; return random.nextInt(variationCount) + min; } public long generateLong(long min, long max) { long variationCount = max - min + 1; long nextLong = Math.abs(random.nextLong()); return nextLong % variationCount + min; } /** * генерация случайного вещественного числа в заданном диапазоне * с включением min и max в диапазон */ public double generateDouble(double min, double max) { long base = (long) Math.pow(10, 8); long minLong = (long) (min * base); long maxLong = (long) (max * base); long randomLong = generateLong(minLong, maxLong); return randomLong * 1.0 / base; } } |
Теперь мы сможем использовать этот класс для генерации чисел в заданных рамках. Протестируем функционал класса в методе main():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import java.util.Scanner; public class MyProject { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); Randomizer randomizer = new Randomizer(); final int NUMBERS_TO_GENERATE = 5; System.out.print("Введите нижнюю границу -> "); int bottomInt = scanner.nextInt(); System.out.print("Введите верхнюю границу -> "); int topInt = scanner.nextInt(); System.out.println("* Генерация случайных целых чисел *"); for (int i = 0; i < NUMBERS_TO_GENERATE; i++) { System.out.print(randomizer.generateInt(bottomInt, topInt) + " "); } System.out.println(); System.out.println("* Генерация случайных дробных чисел *"); for (int i = 0; i < NUMBERS_TO_GENERATE; i++) { System.out.print(randomizer.generateDouble(bottomInt, topInt) + " "); } } } |
Задав границы [-2; 4], мы получили 5 целых и 5 вещественных чисел в этом диапазоне. Это достаточно удобно. Такой класс можно было бы использовать в разных программах: играх, социальных сетях и даже гороскопах.
Math.random()
В Java существует класс Math, который предоставляет функционал по многим необходимым математическим вычислениям, от возведения в степень до вычисления синуса. В данный момент этот класс нам интересен тем, что он также умеет генерировать случайные числа. Правда, делает он это так же, как и random.nextDouble(), так что для удобного использования тоже придётся написать самостоятельно парочку методов.
Напишем сперва простую программу, использующую этот самый Math.random():
1 2 3 4 5 6 7 |
public class MyProject { public static void main(String[] args) { for (int i = 0; i < 3; i++) { System.out.println("Очередное случайное число: " + Math.random()); } } } |
При каждом перезапуске программы числа генерироваться будут разные. Но метод Math.random(), опять же, какой-то сырой. Ну где может пригодиться генерирование исключительно от 0 до 1? Давайте переделаем класс Randomizer под использование метода Math.random().
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
import java.util.Random; /** * класс для удобной генерации (псевдо)случайных чисел * * @author Катя Левкович * @version 1.1, 07.09.2023 */ public class Randomizer { /** * генерация случайного целого числа int в заданном диапазоне * с включением min и max в диапазон */ public int generateInt(int min, int max) { int variationCount = max - min + 1; int nextInt = (int) (Math.random() * variationCount); return nextInt + min; } public long generateLong(long min, long max) { long variationCount = max - min + 1; long nextLong = (long) (Math.random() * variationCount); return nextLong + min; } /** * генерация случайного вещественного числа в заданном диапазоне * с включением min и max в диапазон */ public double generateDouble(double min, double max) { long base = (long) Math.pow(10, 8); long minLong = (long) (min * base); long maxLong = (long) (max * base); long randomLong = generateLong(minLong, maxLong); return randomLong * 1.0 / base; } } |
Заметьте, какова структура получившейся программы: для внесения изменений способа генерации случайных чисел нам пришлось поменять только два метода класса Randomizer – generateInt() и generateLong(). Никакие внешние классы и методы (в том числе метод main(), откуда мы тестируем класс Randomizer) и не подозревают о внесённых изменениях. Вот это вежливое обращение с кодом. Итого, в метод main() не нужно вносить абсолютно никаких изменений. Это пахнет инкапсуляцией из ООП.
Запустим программу, чтобы убедиться, что числа исправно генерируются в заданном диапазоне:
Да, всё работает ровно так, как ожидается, а это – главная цель программистов. В диапазоне [100; 200] генерируются как целые, так и вещественные числа, причём разница во внутренней реализации класса Randomizer не видна стороннему пользователю – для него ничего не изменилось.
Теперь, имея понимание работы генераторов случайных чисел, вы можете успешно использовать их в проектах, добавляя случайность и разнообразие в программу.