Рішення Марка (прийняте рішення) майже ідеально.
int x;
do {
x = rand();
} while (x >= (RAND_MAX - RAND_MAX % n));
x %= n;
відредаговано 25 березня 1616 о 23:16
Марк Амери 39k21170211
Однак він має застереження, яке відкидає 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: Коли підрахунок відхилених значень (I) = N (Кількість дійсних результатів), то Дійсний набір повернутих значень буде відкинутий вихідною функцією.
Якщо описати різницю значень N і RM як D, тобто:
D = (RM - N)
Тоді як значення D стає меншим, відсоток непотрібних повторних роликів завдяки цьому методу збільшується з кожним природним мультиплікативним. (Коли RAND_MAX НЕ дорівнює простому номеру, це викликає повагу)
EG:
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.
Версія для позначки:
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)
напевно, він би не працював; тому розрахунок Марка залишається найбезпечнішим виконанням.