Perché sto ottenendo risultati non uniformemente distribuiti quando utilizzo $ RANDOM?


14

Ho letto di GNU su Wikipedia e $RANDOMfunzionano su TLDP ma non spiega davvero questo risultato:

$ max=$((6*3600))
$ for f in {1..100000}; do echo $(($RANDOM%max/3600)); done | sort | uniq -c
  21787 0
  22114 1
  21933 2
  12157 3
  10938 4
  11071 5

Perché i valori sopra circa 2 volte di più sono inclinati a 0, 1, 2 rispetto a 3, 4, 5 ma quando cambio il modulo massimo sono quasi equamente distribuiti su tutti e 10 i valori?

$ max=$((9*3600))
$ for f in {1..100000}; do echo $(($RANDOM%max/3600)); done | sort | uniq -c
  11940 0
  11199 1
  10898 2
  10945 3
  11239 4
  10928 5
  10875 6
  10759 7
  11217 8

9
La solita risposta a questo è di ripetere il rilancio (scartare il numero ricevuto e sceglierne un altro) se ci si trova tra il valore massimo per RANDOM e il valore più alto possibile che può dividere equamente nel modulo. Questo non è solito-su-RANDOM, è solito-usare-modulo-per-limitare-dominio RNG in tutte le lingue / strumenti / ecc. l'implementazione di RNG di quel tipo.
Charles Duffy,

7
Vedi il mio articolo del 2013 sulla fonte di questo pregiudizio se vuoi alcuni bei grafici di quanto sia brutto: ericlippert.com/2013/12/16/…
Eric Lippert,

1
"La generazione di numeri casuali è troppo importante per essere lasciata al caso." - Robert Coveyou. Cordiali saluti però: la maggior parte dei programmi non è in grado di generare numeri veramente casuali
jesse_b

@Eric Lippert grazie, lo leggerò volentieri!
cprn,

1
Si noti che, anche se si riscontrano problemi dovuti a distorsioni del modulo, la $RANDOMvariabile non utilizza un buon PRNG internamente.
foresta

Risposte:


36

Per espandere l'argomento della distorsione del modulo, la formula è:

max=$((6*3600))
$(($RANDOM%max/3600))

E in questa formula, $RANDOMè un valore casuale nell'intervallo 0-32767.

   RANDOM Each time this parameter is referenced, a random integer between
          0 and 32767 is generated.

Aiuta a visualizzare come questo si associa a possibili valori:

0 = 0-3599
1 = 3600-7199
2 = 7200-10799
3 = 10800-14399
4 = 14400-17999
5 = 18000-21599
0 = 21600-25199
1 = 25200-28799
2 = 28800-32399
3 = 32400-32767

Quindi, nella tua formula, la probabilità di 0, 1, 2 è doppia rispetto a 4, 5. E anche la probabilità di 3 è leggermente superiore a 4, 5. Da qui il tuo risultato con 0, 1, 2 come vincitori e 4, 5 come perdenti.

Quando si cambia in 9*3600, si presenta come:

0 = 0-3599
1 = 3600-7199
2 = 7200-10799
3 = 10800-14399
4 = 14400-17999
5 = 18000-21599
6 = 21600-25199
7 = 25200-28799
8 = 28800-32399
0 = 32400-32767

1-8 hanno la stessa probabilità, ma c'è ancora una leggera propensione per 0, e quindi 0 è stato ancora il vincitore nel tuo test con 100.000 iterazioni.

Per correggere la distorsione del modulo, dovresti prima semplificare la formula (se vuoi solo 0-5, allora il modulo è 6, non 3600 o anche un numero più folle, non ha senso in questo). Questa semplificazione da sola ridurrà di molto la tua propensione (32766 mappe a 0, 32767 a 1 dando una leggera propensione a quei due numeri).

Per eliminare del tutto il pregiudizio, è necessario ripetere il rollback (ad esempio) quando $RANDOMè inferiore a 32768 % 6(eliminare gli stati che non si associano perfettamente all'intervallo casuale disponibile).

max=6
for f in {1..100000}
do
    r=$RANDOM
    while [ $r -lt $((32768 % $max)) ]; do r=$RANDOM; done
    echo $(($r%max))
done | sort | uniq -c | sort -n

Risultato della prova:

  16425 5
  16515 1
  16720 0
  16769 2
  16776 4
  16795 3

L'alternativa sarebbe usare una diversa fonte casuale che non abbia una distorsione evidente (ordini di grandezza maggiori di soli 32768 valori possibili). Tuttavia, implementare una logica di ripetizione non fa male (anche se probabilmente non avverrà mai).


La tua risposta è in gran parte corretta, tranne: "devi ripetere il rollback quando $ RANDOM è inferiore a 32768% 6" dovrebbe effettivamente essere "uguale o maggiore di floor ((RANDMAX + 1) / 6) * 6" (cioè 32766 ) e correggi il codice di shell associato sotto quello.
Nayuki,

@Nayuki se puoi sottolineare un errore specifico (che si applica nel contesto dato) sarò felice di correggerlo. La mia soluzione è solo un esempio, ci sono diversi modi per farlo. È possibile rimuovere la distorsione dall'intervallo iniziale o finale, o da qualche parte nel mezzo, non fa differenza. Puoi calcolarlo meglio (e non fare un modulo in ogni iterazione). Puoi gestire casi speciali come moduli arbitrari e valori di randmax, anche gestire RANDMAX = INTMAX dove RANDMAX + 1 non esiste, ma non era questo il punto focale.
frostschutz,

La tua risposta è significativamente peggiore del tuo post. Prima di tutto, ho sottolineato in modo specifico quale tua frase sia effettivamente errata. Tieni presente che "32768% 6" == 2, quindi desideri ripetere il riesame ogni volta che $ RANDOM <2? Per quanto riguarda il bias all'inizio / alla fine / al centro dell'intervallo, il tuo intero post riguarda la rimozione del bias alla fine dell'intervallo e anche la mia risposta si rivolge esattamente a questo. Terzo, parli della gestione di RANDMAX = INTMAX, ma nella tua risposta hai menzionato più volte il valore 32768 (= 32767 + 1), il che implica che ti senti a tuo agio con il calcolo di RANDMAX + 1.
Nayuki,

1
@Nayuki il mio codice rimuove 0 e 1, il tuo rimuove 32766 e 32767 e vorrei che tu elaborassi: che differenza fa? Sono solo umano, faccio errori, ma tutto quello che hai detto finora è "è sbagliato" senza spiegare o mostrare il perché. Grazie.
Frostschutz,

1
Non importa, capito. Mi dispiace per il falso allarme.
Nayuki,

23

Questo è pregiudizio del modulo. Se RANDOMè ben costruito, ogni valore compreso tra 0 e 32767 viene prodotto con uguale probabilità. Quando si utilizza il modulo, si cambiano le probabilità: le probabilità di tutti i valori sopra il modulo vengono aggiunte ai valori su cui si mappano.

Nel tuo esempio, 6 × 3600 è circa i due terzi dell'intervallo di valori. Le probabilità del terzo superiore vengono quindi aggiunte a quelle del terzo inferiore, il che significa che i valori da 0 a 2 (circa) hanno il doppio delle probabilità di essere prodotti rispetto ai valori da 3 a 5. 9 × 3600 è quasi 32767, quindi il la polarizzazione del modulo è molto più piccola e influisce solo sui valori compresi tra 32400 e 32767.

Per rispondere alla tua domanda principale, almeno in Bash la sequenza casuale è completamente prevedibile se conosci il seme. Vedi intrand32in variables.c.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.