L2CCCP
9 уровень
Калининград

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

Пост из группы Архив info.javarush.ru
3272 участников
Привет по ту сторону экрана. Любой из нас рано или поздно встречается с необходимостью генерировать случайное число в заданном нами диапазоне будь то вещественное или целое число. Для чего? На самом деле это не важно, это может быть функция для расчета шанса для запуска какого нибудь события, получение случайного множителя или любого другая. Итак, для чего это нужно разобрались, а именно для чего угодно :) На самом деле методов получения псевдослучайного числа очень много, я же приведу пример с классом 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 ;) Еще пару моих статей: Что такое инкрементирование и декрементирование Оператор деления по модулю
Комментарии (13)
  • популярные
  • новые
  • старые
Для того, что бы оставить комментарий вы должны авторизироваться
alexnjc 31 уровень
11 июня 2015, 11:59
Кстати, с помощью рандома можно генерировать пароли.
Простой пример:
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);
    }
}
Izhak 22 уровень, Москва
9 июня 2015, 03:41
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 9 уровень, Калининград
9 июня 2015, 03:54
Класс математики использует как раз объект класса 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 21 уровень
9 июня 2015, 13:26
я прям получил удовольствие когда прочитал, что в стандартной библиотеке есть аналог твоего примера :D
L2CCCP 9 уровень, Калининград
9 июня 2015, 18:28
Вы не поняли на что я ответил, Izhak изменил свой комментарий.
sem_top7 32 уровень
8 июня 2015, 20:07
А числа то не случайные :) Может автор расскажет почему эти числа называются псевдослучайными? И как же все таки добиться случайных чисел?
L2CCCP 9 уровень, Калининград
9 июня 2015, 06:22
А числа то не случайные :)
Как по мне случайность понятие относительное.

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

И как же все таки добиться случайных чисел?
Советую почитать: habrahabr.ru/post/151187/
timurnav 21 уровень
8 июня 2015, 11:31
Предлагаю использовать не приведение к целочисленному типу с инкрементом, а воспользоваться методом того же класса Math.round(n) — стандартное округление в сторону ближнего своего :)
L2CCCP 9 уровень, Калининград
8 июня 2015, 17:41
Да можно, но разницы честно говоря тут особой нету.

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 9 уровень, Калининград
8 июня 2015, 18:00
Можно легко сравнить что будет работать быстрее.
Вот работа математического округления.
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 21 уровень
8 июня 2015, 18:34
можешь гордиться собой :)
L2CCCP 9 уровень, Калининград
8 июня 2015, 18:48
К чему это сообщение? :)
timurnav 21 уровень
9 июня 2015, 13:21
к чему это сообщение? к твоему ответу на мое сообщение :)