Генерировать случайные числа в указанном диапазоне - различные случаи (int, float, inclusive, exclusive)
задана функция Math.random()
, которая возвращает число между значениями [0,1) и min
max
, чтобы указать диапазон, как мы можем генерировать числа для следующих случаев:
Мы хотим целое число:
-
A: (min,max) ?
-
B: [min,max) return Math.floor(Math.random() * (max - min)) + min;
-
C: (min,max] ?
-
D: [min,max] return Math.floor(Math.random() * (max - min + 1)) + min;
Мы хотим float:
-
A: (min,max) ?
-
B: [min,max) return Math.random() * (max - min) + min;
-
C: (min,max] ?
-
D: [min,max] ?
Ответы
Ответ 1
Целые
Ваша формула для B. правильная, все остальное получается тривиальными исправлениями +1
-1
:
- A.
(min, max) = [min + 1, max)
, поэтому из B. получаем
min + 1 + Math.floor(Math.random() * (max - min - 1))
- В.
min + Math.floor(Math.random() * (max - min))
- С. Так как в интервале арифметики
(min, max] = max - [0, max - min)
можно было бы написать max - Math.floor(Math.random() * (max - min))
- D.
[min, max] = [min, max + 1)
, поэтому: min + Math.floor(Math.random() * (max + 1 - min))
Поплавки.. Как уже указывал V13, вопрос несколько некорректен: если мы рассматриваем отдельные точки как множества с нулем, то почти (в измеренном теоретическом смысле) нет разницы между четыре набора... Однако, если вы хотите гарантировать, что исключенные интервальные границы никогда (а не просто "почти никогда" ) не будут выбраны, и если вы предполагаете, что ошибок округления нет, вы можете сделать что-то вроде этого:
-
A: var middle = (min + max) / 2; var sign = Math.random() > 0.5 ? 1 : -1; return middle + sign * (max - min) / 2 * Math.random();
Это решение наносит меньшую массу на 0
, но это должно быть незначительным для всех практических целей.
-
B: min + Math.random() * (max - min)
, да.
- C:
max - Math.random() * (max - min)
, симметричный выше.
- D: Невозможно гарантировать, что мы когда-либо ударим верхнюю границу интервала, поэтому мы можем просто использовать
min + Math.random() * (max - min)
.
Разница между A и D заключается в следующем: если мы попытались использовать формулу min + Math.random() * (max - min)
в A, мы могли бы иногда получить a 0
(потому что диапазон возможных чисел на самом деле конечен). Однако никакая разумная статистика никогда не могла бы жаловаться, что верхняя граница не попадает в D.
Ответ 2
Integer:
- A:
return Math.floor(Math.random() * (max - min - 1)) + min + 1;
- B: Правильно
- C: Это то же самое, что и [min + 1, max + 1), поэтому:
return Math.floor(Math.random() * (max - min)) + min + 1;
- D: Правильно
Для поплавков вам нужно знать, какая у вас арифметика с плавающей запятой. Если вы не используете специальную библиотеку, равенство в float - это то, чего обычно не бывает, поэтому нет смысла иметь замкнутые диапазоны. Как таковая не должно быть никакой разницы между всеми четырьмя, и вы можете просто пойти с:
return Math.random() * (max-min) + min;
Чтобы вопрос имел смысл, вам нужно определить минимально допустимый диапазон для равенства (например, r=0.00000000000000001
). После этого вы можете преобразовать ваши уравнения открытого диапазона (т.е. (min, max)
) в [min+r, max-r]
.
Ответ 3
Начну с определения следующих двух вспомогательных функций, а затем используйте их для получения значений. Эти методы являются вашими определениями случаев B.
int nextInt(int min, int max) {
return Math.floor(Math.random() * (max - min)) + min;
}
float nextFloat(float min, float max) {
return Math.random() * (max - min) + min;
}
Затем для целых
- A: return nextInt (min + 1, max);
- B: return nextInt (min, max);
- C: return nextInt (min + 1, max + 1);
- D: return nextInt (min, max + 1);
Поплавки - это более сложный случай. Некоторые могут утверждать, что нет большой разницы в том, включены ли конечные точки или нет - особенно закрытое решение может быть использовано вместо закрытого - поскольку конечные точки редко выбираются. Но поскольку вполне возможно реализовать все сценарии, я думаю, что есть математический интерес к тому, как это можно сделать.
A: В этом случае мы можем просто убедиться, что незаконное значение снова свернуто:
float f;
do {
f = nextFloat(min, max);
} while (f == min);
return f;
В:
return nextFloat(min, max);
C: Здесь мы просто переключаем конечную точку
float f = nextFloat(min, max);
if (f == min) {
return max;
}
return f;
D: Это самый сложный сценарий для всех, но может быть достигнут следующим образом:
float f = nextFloat(min, max);
if (f == min) {
return max;
}
return nextFloat(min, max);
Случаи A и D немного грязны в том смысле, что им может потребоваться генерация более одного случайного числа, что может быть проблемой в некоторых конкретных сценариях. Решение этого требует глубокого углубления спецификации спецификации с плавающей точкой для поиска альтернативных реализаций. Далее следует отметить, что в случае D способность к максимизации имеет очень немного более высокую проницаемость, чем любое другое число, если ссылочная функция полностью однородна (обычно нет), но обычно это только теоретический вопрос. (Если быть точным, если в пределах диапазона имеется n возможных значений, то возможностоимость максимального значения pmax = 1/(n-1) и прочность любого другого значения равна (1-pmax)/(n-1)).
Следует отметить небольшую проблему, которая должна быть учтена при точных реализациях с плавающей запятой A. Существует вероятность, что вызывающая функция вызовет ее со смежными плавающими точками. Это нелегко увидеть при помощи какой-либо фиктивной проверки параметров, поэтому, чтобы быть в безопасности, должно быть ограничение на то, сколько раз цикл может быть выполнен.