Решение Марка (принятое решение) почти идеально.
int x;
do {
x = rand();
} while (x >= (RAND_MAX - RAND_MAX % n));
x %= n;
отредактировано 25 марта 16 в 23:16
Марк Амери 39к21170211
Тем не менее, он имеет оговорку, которая отбрасывает 1 действительный набор результатов в любом сценарии, где RAND_MAX
( RM
) на 1 меньше, чем кратное N
(гдеN
= количество возможных действительных результатов).
т. е. когда «количество сброшенных значений» ( D
) равно N
, тогда они фактически являются допустимым набором (а V)
не недействительным набором ( I
).
Причиной этого является то, что в какой-то момент Марк теряет из виду разницу между N
и Rand_Max
.
N
это набор действительных членов, состоящий только из положительных целых чисел, поскольку он содержит количество ответов, которые были бы действительными. (например: Set N
= {1, 2, 3, ... n }
)
Rand_max
Однако это набор, который (как определено для наших целей) включает любое количество неотрицательных целых чисел.
В его наиболее общей форме, что определяется здесь как Rand Max
набор всех действительных результатов, которые теоретически могут включать отрицательные числа или нечисловые значения.
Поэтому Rand_Max
лучше определить его как «Возможные ответы».
Однако N
работает против количества значений в наборе допустимых ответов, поэтому даже как определено в нашем конкретном случае, Rand_Max
будет значение на единицу меньше, чем общее число, которое он содержит.
Используя решение Марка, значения отбрасываются, когда: X => RM - RM% N
EG:
Ran Max Value (RM) = 255
Valid Outcome (N) = 4
When X => 252, Discarded values for X are: 252, 253, 254, 255
So, if Random Value Selected (X) = {252, 253, 254, 255}
Number of discarded Values (I) = RM % N + 1 == N
IE:
I = RM % N + 1
I = 255 % 4 + 1
I = 3 + 1
I = 4
X => ( RM - RM % N )
255 => (255 - 255 % 4)
255 => (255 - 3)
255 => (252)
Discard Returns $True
Как вы можете видеть в приведенном выше примере, когда значение X (случайное число, которое мы получаем из начальной функции) равно 252, 253, 254 или 255, мы отбрасываем его, даже если эти четыре значения составляют действительный набор возвращаемых значений ,
IE: когда счетчик значений Discarded (I) = N (Количество действительных результатов), то Действительный набор возвращаемых значений будет отброшен исходной функцией.
Если мы опишем разницу между значениями N и RM как D, то есть:
D = (RM - N)
Затем, когда значение D становится меньше, Процент ненужных повторных бросков из-за этого метода увеличивается при каждом естественном мультипликате. (Когда RAND_MAX НЕ равен простому числу, это имеет значение)
НАПРИМЕР:
RM=255 , N=2 Then: D = 253, Lost percentage = 0.78125%
RM=255 , N=4 Then: D = 251, Lost percentage = 1.5625%
RM=255 , N=8 Then: D = 247, Lost percentage = 3.125%
RM=255 , N=16 Then: D = 239, Lost percentage = 6.25%
RM=255 , N=32 Then: D = 223, Lost percentage = 12.5%
RM=255 , N=64 Then: D = 191, Lost percentage = 25%
RM=255 , N= 128 Then D = 127, Lost percentage = 50%
Поскольку процент необходимых Rerolls увеличивается по мере приближения N к RM, это может иметь значение для многих различных значений в зависимости от ограничений системы, в которой он работает, и от искомых значений.
Чтобы отрицать это, мы можем внести простую поправку, как показано здесь:
int x;
do {
x = rand();
} while (x > (RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n) );
x %= n;
Это обеспечивает более общую версию формулы, которая учитывает дополнительные особенности использования модуля для определения ваших максимальных значений.
Примеры использования небольшого значения для RAND_MAX, которое является мультипликативным для N.
Mark'original Версия:
RAND_MAX = 3, n = 2, Values in RAND_MAX = 0,1,2,3, Valid Sets = 0,1 and 2,3.
When X >= (RAND_MAX - ( RAND_MAX % n ) )
When X >= 2 the value will be discarded, even though the set is valid.
Обобщенная версия 1:
RAND_MAX = 3, n = 2, Values in RAND_MAX = 0,1,2,3, Valid Sets = 0,1 and 2,3.
When X > (RAND_MAX - ( ( RAND_MAX % n ) + 1 ) % n )
When X > 3 the value would be discarded, but this is not a vlue in the set RAND_MAX so there will be no discard.
Кроме того, в случае, когда N должно быть числом значений в RAND_MAX; в этом случае вы можете установить N = RAND_MAX +1, если только RAND_MAX = INT_MAX.
По циклу вы можете просто использовать N = 1, и любое значение X будет, тем не менее, принято и вставлять оператор IF для вашего окончательного множителя. Но, возможно, у вас есть код, который может иметь вескую причину для возврата 1, когда функция вызывается с n = 1 ...
Поэтому может быть лучше использовать 0, что обычно дает ошибку Div 0, когда вы хотите иметь n = RAND_MAX + 1
Обобщенная версия 2:
int x;
if n != 0 {
do {
x = rand();
} while (x > (RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n) );
x %= n;
} else {
x = rand();
}
Оба эти решения решают проблему с ненужными отклоненными действительными результатами, которые произойдут, когда RM + 1 является произведением n.
Вторая версия также охватывает сценарий крайнего случая, когда вам нужно n, чтобы равняться общему возможному набору значений, содержащихся в RAND_MAX.
Модифицированный подход в обоих случаях одинаков и позволяет найти более общее решение необходимости предоставления действительных случайных чисел и минимизации отброшенных значений.
Чтобы повторить:
Основное общее решение, которое расширяет пример знака:
// Assumes:
// RAND_MAX is a globally defined constant, returned from the environment.
// int n; // User input, or externally defined, number of valid choices.
int x;
do {
x = rand();
} while (x > (RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n) ) );
x %= n;
Расширенное общее решение, которое допускает один дополнительный сценарий RAND_MAX + 1 = n:
// Assumes:
// RAND_MAX is a globally defined constant, returned from the environment.
// int n; // User input, or externally defined, number of valid choices.
int x;
if n != 0 {
do {
x = rand();
} while (x > (RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n) ) );
x %= n;
} else {
x = rand();
}
В некоторых языках (особенно в интерпретируемых) выполнение вычислений операции сравнения вне условия while может привести к более быстрым результатам, поскольку это однократное вычисление независимо от того, сколько повторных попыток требуется. YMMV!
// Assumes:
// RAND_MAX is a globally defined constant, returned from the environment.
// int n; // User input, or externally defined, number of valid choices.
int x; // Resulting random number
int y; // One-time calculation of the compare value for x
if n != 0 {
y = RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n)
do {
x = rand();
} while (x > y);
x %= n;
} else {
x = rand();
}
RAND_MAX%n == n - 1
_ _ есть(RAND_MAX + 1) % n == 0
. При чтении кода я склонен понимать его% something == 0
как «равномерно делимый» с большей готовностью, чем другие способы его вычисления. Конечно, если ваш C ++ stdlib имеетRAND_MAX
то же значение, что иINT_MAX
,(RAND_MAX + 1)
конечно, не будет работать; поэтому расчет Марка остается самой безопасной реализацией.