Rozwiązanie Marka (zaakceptowane rozwiązanie) jest prawie idealne.
int x;
do {
x = rand();
} while (x >= (RAND_MAX - RAND_MAX % n));
x %= n;
zredagowano 25 marca 16 o 23:16
Mark Amery 39k21170211
Ma jednak zastrzeżenie, które odrzuca 1 prawidłowy zestaw wyników w każdym scenariuszu, w którym RAND_MAX
( RM
) jest o 1 mniejszy niż wielokrotność N
(gdzie N
= liczba możliwych ważnych wyników).
tzn. gdy „liczba odrzuconych wartości” ( D
) jest równa N
, to w rzeczywistości są one prawidłowym zestawem (a V)
nie niepoprawnym zestawem ( I
).
Co powoduje, że w pewnym momencie Mark traci widoczność różnicy między N
i Rand_Max
.
N
jest zbiorem, którego poprawni członkowie składają się tylko z dodatnich liczb całkowitych, ponieważ zawiera liczbę poprawnych odpowiedzi. (np .: Set N
= {1, 2, 3, ... n }
)
Rand_max
Jest to jednak zestaw, który (jak zdefiniowano dla naszych celów) zawiera dowolną liczbę liczb całkowitych nieujemnych.
W najogólniejszej formie zdefiniowano tu Rand Max
zbiór wszystkich ważnych wyników, które teoretycznie mogą obejmować liczby ujemne lub wartości nienumeryczne.
Dlatego Rand_Max
jest lepiej zdefiniowany jako zestaw „możliwych odpowiedzi”.
N
Działa jednak w stosunku do liczby wartości w zestawie prawidłowych odpowiedzi, więc nawet jak zdefiniowano w naszym konkretnym przypadku, Rand_Max
wartość będzie o jeden mniejsza niż całkowita liczba, którą zawiera.
Korzystając z rozwiązania Marka, wartości są odrzucane, gdy: 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
Jak widać w powyższym przykładzie, gdy wartość X (liczba losowa, którą otrzymujemy z funkcji początkowej) wynosi 252, 253, 254 lub 255, odrzucilibyśmy ją, mimo że te cztery wartości zawierają prawidłowy zestaw zwracanych wartości .
IE: Gdy liczba wartości odrzuconych (I) = N (liczba prawidłowych wyników), wówczas prawidłowy zestaw wartości zwracanych zostanie odrzucony przez funkcję oryginalną.
Jeśli opisamy różnicę między wartościami N i RM jako D, tj .:
D = (RM - N)
Następnie, gdy wartość D staje się mniejsza, procent niepotrzebnych przerzutów z powodu tej metody wzrasta przy każdym naturalnym mnożeniu. (Gdy RAND_MAX NIE jest równe liczbie pierwszej, jest to ważne)
NA PRZYKŁAD:
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%
Ponieważ procent potrzebnej liczby ponownych zapytań wzrasta, im bliżej N dochodzi do RM, może to mieć znaczenie przy wielu różnych wartościach, w zależności od ograniczeń systemu z uruchomionym kodem i poszukiwanych wartości.
Aby temu zaradzić, możemy wprowadzić prostą poprawkę Jak pokazano tutaj:
int x;
do {
x = rand();
} while (x > (RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n) );
x %= n;
Zapewnia to bardziej ogólną wersję formuły, która uwzględnia dodatkowe osobliwości związane z używaniem modułu do definiowania maksymalnych wartości.
Przykłady użycia małej wartości dla RAND_MAX, która jest wielokrotnością N.
Oryginalna wersja Marka:
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.
Uogólniona wersja 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.
Dodatkowo w przypadku, gdy N powinna być liczbą wartości w RAND_MAX; w takim przypadku możesz ustawić N = RAND_MAX +1, chyba że RAND_MAX = INT_MAX.
Jeśli chodzi o pętle, możesz po prostu użyć N = 1, a każda wartość X zostanie jednak zaakceptowana i umieścisz instrukcję IF w swoim ostatecznym mnożniku. Ale może masz kod, który może mieć prawidłowy powód zwrócenia 1, gdy funkcja jest wywoływana z n = 1 ...
Dlatego może być lepiej użyć 0, które normalnie zapewnia błąd Div 0, jeśli chcesz mieć n = RAND_MAX + 1
Uogólniona wersja 2:
int x;
if n != 0 {
do {
x = rand();
} while (x > (RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n) );
x %= n;
} else {
x = rand();
}
Oba te rozwiązania rozwiązują problem, niepotrzebnie odrzucając prawidłowe wyniki, które pojawią się, gdy RM + 1 będzie iloczynem n.
Druga wersja obejmuje również scenariusz przypadków skrajnych, gdy potrzebujesz n, aby zrównoważyć całkowity możliwy zestaw wartości zawartych w RAND_MAX.
Zmodyfikowane podejście w obu przypadkach jest takie samo i pozwala na bardziej ogólne rozwiązanie potrzeby zapewnienia prawidłowych liczb losowych i minimalizacji odrzuconych wartości.
Powtarzać:
Podstawowe ogólne rozwiązanie rozszerzające przykład znaku:
// 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;
Rozszerzone ogólne rozwiązanie, które umożliwia jeden dodatkowy scenariusz 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();
}
W niektórych językach (szczególnie językach interpretowanych) wykonywanie obliczeń operacji porównania poza czasem while może prowadzić do szybszych wyników, ponieważ jest to obliczenie jednorazowe, bez względu na to, ile ponownych prób jest wymaganych. 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
_ _ jest(RAND_MAX + 1) % n == 0
. Czytając kod, rozumiem go% something == 0
jako „równomiernie podzielny” łatwiej niż inne sposoby jego obliczania. Oczywiście, jeśli twój stdlib w C ++ maRAND_MAX
taką samą wartość jakINT_MAX
, na(RAND_MAX + 1)
pewno nie zadziała; więc obliczenia Marka pozostają najbezpieczniejszą implementacją.