• ,

Генерация случайного числа в заданном диапазоне

Привет по ту сторону экрана.

Любой из нас рано или поздно встречается с необходимостью генерировать случайное число в заданном нами диапазоне будь то вещественное или целое число.

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

Итак, для чего это нужно разобрались, а именно для чего угодно :)

На самом деле методов получения псевдослучайного числа очень много, я же приведу пример с классом Math, а именно с методом random();

Что же мы имеем?
Вызов метода Math.random() возвращает псевдослучайное вещественное число (double) из диапазона [0;1), то есть, от 0 до 1 исключая 1, а значит максимальное число в диапазоне это 0.99999999999...

Хорошо, мы получили псевдослучайное число, но если нам нужен свой диапазон?
К примеру, нам нужно псевдослучайное число из диапазона [0;100)?

Пишем код:

	public static void main(String... args)
	{
		final double max = 100.; // Максимальное число для диапазона от 0 до max
		final double rnd = rnd(max);

		System.out.println("Псевдослучайное вещественное число: " + rnd);
	}

	/**
	 * Метод получения псевдослучайного вещественного числа от 0 до max (исключая max);
	 */
	public static double rnd(final double max)
	{
		return Math.random() * max;
	}


Получилось не плохо, но max (в нашем случае) мы все равно не получим.
Для того чтобы получить случайное число в диапазоне [0;100] нам необходимо прибавить к нашему max 1, а затем преобразовать в целое число типа int или long (зависит от диапазонов которые Вы будете использовать).

Пишем код:

	public static void main(String... args)
	{
		final int max = 100; // Максимальное число для диапазона от 0 до max
		final int rnd = rnd(max);

		System.out.println("Псевдослучайное целое число: " + rnd);
	}

	/**
	 * Метод получения псевдослучайного целого числа от 0 до max (включая max);
	 */
	public static int rnd(int max)
	{
		return (int) (Math.random() * ++max);
	}



Примечание: Как видите переменная max была инкрементирована префиксной формой. (Если Вы не знаете что это советую почитать мою статью)

Отлично, мы получили то что хотели, но если нам нужен диапазон не от 0, а к примеру [10;75]

Пишем код:

	public static void main(String... args)
	{
		final int min = 10; // Минимальное число для диапазона
		final int max = 75; // Максимальное число для диапазона
		final int rnd = rnd(min, max);

		System.out.println("Псевдослучайное целое число: " + rnd);
	}

	/**
	 * Метод получения псевдослучайного целого числа от min до max (включая max);
	 */
	public static int rnd(int min, int max)
	{
		max -= min;
		return (int) (Math.random() * ++max) + min;
	}



Разбор кода из метода rnd:
Минимальное число диапазона = 10;
Максимальное число диапазона = 75;
max -= min; // Отнимаем от максимального значения минимальное для получения множителя псевдослучайного вещественного числа.
Максимальное число после расчета равно 65

Псевдослучайное вещественное число (к примеру) равно 0.18283417347179454 (Был получен при вызове Math.random())
Максимальное число перед умножением было инкрементировано префиксной формой.
Максимальное число теперь 66

Умножаем 0.18283417347179454 на 66
Результат умножения равен 12.06705544913844
Преобразовываем результат умножения максимального числа на псевдослучайное вещественное число к типу целого числа int
Прибавляем минимальное число к преобразованному результату который равен 12
Возвращаем результат: 22

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

Надеюсь для Вас это было полезно и познавательно.

Успехов в освоении Java ;)

Еще пару моих статей:
Что такое инкрементирование и декрементирование
Оператор деления по модулю

14 комментариев

timurnav
Предлагаю использовать не приведение к целочисленному типу с инкрементом, а воспользоваться методом того же класса Math.round(n) — стандартное округление в сторону ближнего своего :)
L2CCCP
Да можно, но разницы честно говоря тут особой нету.


	public static void main(String... args)
	{
		final long min = 10; // Минимальное число для диапазона
		final long max = 75; // Максимальное число для диапазона
		final long rnd = rnd(min, max);
		System.out.println("Псевдослучайное целое число: " + rnd);
	}

	/**
	 * Метод получения псевдослучайного целого числа от min до max (включая max);
	 */
	public static long rnd(long min, long max)
	{
		max -= min;
		final double random = Math.random();
		return Math.round((random * max) + min);
	}



Но с такой реализацией код быстрее работать не будет, а даже на оборот.
Вот код метода round(double) из класса математики.


private int round(double d){
    double dAbs = Math.abs(d);
    int i = (int) dAbs;
    double result = dAbs - (double) i;
    if(result<0.5){
        return d<0 ? -i : i;            
    }else{
        return d<0 ? -(i+1) : i+1;          
    }
}



Как видите, в методе на много больше обработки нежели в той реализации что написал я.

Я инкрементирую переменную и преобразовываю ее в тип целого int.
В математическом округлении же, создается 3 переменных, при этом с тем же преобразованием к целому типу для расчета, к тому же делаются лишние расчеты и проверки, лишь для того чтобы произвести сравнение дробной часть.
И это еще не все, в случаи если дробная часть больше или равно .5 то тут возвращается более простая запись инкремента.

Так что спорное предложение :)
L2CCCP
Можно легко сравнить что будет работать быстрее.
Вот работа математического округления.

	public static void main(String... args)
	{
		final long start = System.currentTimeMillis();
		final long min = 10; // Минимальное число для диапазона
		final long max = 75; // Максимальное число для диапазона

		long rnd = min;
		for(int i = 0; i < 1000000000; i++)
			rnd = rnd(min, max);

		System.out.println("Затрачено времени: " + ((double) (System.currentTimeMillis() - start) / 1000D) + " секунд(ы)")
	}

	/**
	 * Метод получения псевдослучайного целого числа от min до max (включая max);
	 */
	public static long rnd(long min, long max)
	{
		max -= min;
		final double random = Math.random();
		return Math.round((random * max) + min);
	}



Результат:
Затрачено времени: 23.292 секунд(ы)

Теперь с обычным инкрементом и преобразованием.


	public static void main(String... args)
	{
		final long start = System.currentTimeMillis();
		final long min = 10; // Минимальное число для диапазона
		final long max = 75; // Максимальное число для диапазона

		long rnd = min;
		for(int i = 0; i < 1000000000; i++)
			rnd = rnd(min, max);

		System.out.println("Затрачено времени: " + ((double) (System.currentTimeMillis() - start) / 1000D) + " секунд(ы)");
	}

	/**
	 * Метод получения псевдослучайного целого числа от min до max (включая max);
	 */
	public static long rnd(long min, long max)
	{
		max -= min;
		return (long) (Math.random() * ++max) + min;
	}



Результат:
Затрачено времени: 19.665 секунд(ы)

Разница весьма большая.
timurnav
можешь гордиться собой :)
L2CCCP
К чему это сообщение? :)
timurnav
к чему это сообщение? к твоему ответу на мое сообщение :)
sem_top7
А числа то не случайные :) Может автор расскажет почему эти числа называются псевдослучайными? И как же все таки добиться случайных чисел?
L2CCCP
А числа то не случайные :)
Как по мне случайность понятие относительное.

Может автор расскажет почему эти числа называются псевдослучайными?
Псевдослучайными их называют из за того что они совсем не случайны, а вычисляются по определенной формуле.

И как же все таки добиться случайных чисел?
Советую почитать: habrahabr.ru/post/151187/
Izhak
  • Izhak
  • 0
  • Комментарий отредактирован 2015-06-09 03:41:25 пользователем Izhak
stackoverflow.com/questions/363681/generating-random-integers-in-a-range-with-java


int r = (new Random()).ints(5,67).iterator().nextInt();

(Можно задать итератор и по нему путешествовать)
Для мультипоточных
docs.oracle.com/javase/tutorial/essential/concurrency/threadlocalrandom.html

int r = ThreadLocalRandom.current().nextInt(4, 77);
L2CCCP
Класс математики использует как раз объект класса Random для получения псевдослучайного вещественного числа.


        public static double random() {
        if (randomNumberGenerator == null) initRNG();
            return randomNumberGenerator.nextDouble();
        }

        private static Random randomNumberGenerator;

        private static synchronized void initRNG() {
        if (randomNumberGenerator == null)
            randomNumberGenerator = new Random();
        }



Так что по факту там описан аналог моего примера :)
timurnav
я прям получил удовольствие когда прочитал, что в стандартной библиотеке есть аналог твоего примера :D
L2CCCP
Вы не поняли на что я ответил, Izhak изменил свой комментарий.
alexnjc
  • alexnjc
  • 0
  • Комментарий отредактирован 2015-06-11 11:59:18 пользователем alexnjc
Кстати, с помощью рандома можно генерировать пароли.
Простой пример:

import java.util.Random;

public class PasswordGenerator {
    public static String generate(int from, int to) {
        String pass  = "";
        Random r     = new Random();
        int cntchars = from + r.nextInt(to - from + 1);

        for (int i = 0; i < cntchars; ++i) {
            char next = 0;
            int range = 10;

            switch(r.nextInt(3)) {
                case 0: {next = '0'; range = 10;} break;
                case 1: {next = 'a'; range = 26;} break;
                case 2: {next = 'A'; range = 26;} break;
            }

            pass += (char)((r.nextInt(range)) + next);
        }

        return pass;
    }

    public static void main(String argv[]) {
        String pass = generate(8, 12);
        System.out.println(pass);
    }
}
Sambalinski
  • Sambalinski
  • 0
  • Комментарий отредактирован 2018-03-27 22:08:16 пользователем Sambalinski
Автор! У тебя опечатка в возвращаемом значении метода
public static int rnd(int min, int max){}
он всегда будет возвращать 0:
return (int) Math.random() * ++max + min;
Потому что сперва к инту будет преобразовываться Math.random()
Нужно поставить скобки
return (int) (Math.random() * ++max) + min;
Спасибо большое за статью! Она помогла мне :)
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.