Mark's Solution (La soluzione accettata) è quasi perfetta.
int x;
do {
x = rand();
} while (x >= (RAND_MAX - RAND_MAX % n));
x %= n;
modificato il 25 marzo 16 alle 23:16
Mark Amery 39k21170211
Tuttavia, ha un avvertimento che scarta 1 serie valida di risultati in qualsiasi scenario in cui RAND_MAX
( RM
) è 1 in meno di un multiplo di N
(Dove N
= il numero di possibili risultati validi).
vale a dire, quando il "conteggio dei valori scartati" ( D
) è uguale a N
, allora sono in realtà un set valido ( V)
, non un set non valido ( I
).
Ciò che provoca questo è che ad un certo punto Mark perde di vista la differenza tra N
e Rand_Max
.
N
è un insieme i cui membri validi sono composti solo da numeri interi positivi, in quanto contiene un conteggio di risposte valide. (ad es .: Set N
= {1, 2, 3, ... n }
)
Rand_max
Tuttavia è un insieme che (come definito per i nostri scopi) include un numero qualsiasi di numeri interi non negativi.
Nella sua forma più generica, ciò che è definito qui Rand Max
è l'insieme di tutti i risultati validi, che potrebbe teoricamente includere numeri negativi o valori non numerici.
Pertanto Rand_Max
è meglio definito come l'insieme di "Risposte possibili".
però N
opera contro il conteggio dei valori all'interno dell'insieme di risposte valide, quindi anche come definito nel nostro caso specifico, Rand_Max
sarà un valore uno in meno del numero totale che contiene.
Usando la soluzione di Mark, i valori vengono scartati quando: 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
Come puoi vedere nell'esempio sopra, quando il valore di X (il numero casuale che otteniamo dalla funzione iniziale) è 252, 253, 254 o 255, lo scartiamo anche se questi quattro valori comprendono un insieme valido di valori restituiti .
IE: quando il conteggio dei valori scartati (I) = N (il numero di risultati validi), un set valido di valori restituiti verrà scartato dalla funzione originale.
Se descriviamo la differenza tra i valori N e RM come D, ovvero:
D = (RM - N)
Quindi, man mano che il valore di D diminuisce, la percentuale di ripetizioni non necessarie a causa di questo metodo aumenta ad ogni moltiplicativo naturale. (Quando RAND_MAX NON è uguale a un numero primo, questo è un problema valido)
PER ESEMPIO:
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%
Poiché la percentuale di rilanci necessari aumenta man mano che N si avvicina a RM, ciò può essere una valida preoccupazione per molti valori diversi a seconda dei vincoli del sistema che esegue il codice e dei valori ricercati.
Per negare questo possiamo fare un semplice emendamento Come mostrato qui:
int x;
do {
x = rand();
} while (x > (RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n) );
x %= n;
Ciò fornisce una versione più generale della formula che tiene conto delle peculiarità aggiuntive dell'uso del modulo per definire i valori massimi.
Esempi di utilizzo di un valore piccolo per RAND_MAX che è un moltiplicativo di N.
Mark'original Version:
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.
Versione generalizzata 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.
Inoltre, nel caso in cui N dovrebbe essere il numero di valori in RAND_MAX; in questo caso, è possibile impostare N = RAND_MAX +1, a meno che RAND_MAX = INT_MAX.
Per quanto riguarda il ciclo, potresti semplicemente usare N = 1, e qualsiasi valore di X sarà comunque accettato e inserirà un'istruzione IF per il moltiplicatore finale. Ma forse hai un codice che potrebbe avere un motivo valido per restituire un 1 quando la funzione viene chiamata con n = 1 ...
Quindi potrebbe essere meglio usare 0, che normalmente fornirebbe un errore Div 0, quando si desidera avere n = RAND_MAX + 1
Versione generalizzata 2:
int x;
if n != 0 {
do {
x = rand();
} while (x > (RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n) );
x %= n;
} else {
x = rand();
}
Entrambe queste soluzioni risolvono il problema con risultati validi inutilmente scartati che si verificano quando RM + 1 è un prodotto di n.
La seconda versione copre anche lo scenario del caso limite quando è necessario n per eguagliare l'insieme totale possibile di valori contenuti in RAND_MAX.
L'approccio modificato in entrambi è lo stesso e consente una soluzione più generale alla necessità di fornire numeri casuali validi e ridurre al minimo i valori scartati.
Reiterare:
La soluzione generale di base che estende l'esempio di mark:
// 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;
La soluzione generale estesa che consente uno scenario aggiuntivo di 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();
}
In alcune lingue (in particolare le lingue interpretate), fare i calcoli dell'operazione di confronto al di fuori della condizione while può portare a risultati più rapidi in quanto si tratta di un calcolo una tantum, indipendentemente dal numero di tentativi richiesti. 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
. Quando leggo il codice, tendo a capire% something == 0
come "uniformemente divisibile" più facilmente rispetto ad altri modi di calcolarlo. Naturalmente, se il tuo C ++ stdlib haRAND_MAX
lo stesso valore diINT_MAX
,(RAND_MAX + 1)
sicuramente non funzionerebbe; quindi il calcolo di Mark rimane l'implementazione più sicura.