Appartenente rand() % n
all'essere meno che ideale
Fare rand() % n
ha una distribuzione non uniforme. Otterrai un numero sproporzionato di determinati valori perché il numero di valori non è un multiplo di 20
Successivamente, in rand()
genere è un generatore congruenziale lineare (ce ne sono molti altri , proprio questo è il più probabilmente implementato - e con parametri meno che ideali (ci sono molti modi per selezionare i parametri)). Il problema più grande con questo è che spesso i bit bassi (quelli che ottieni con % 20
un'espressione di tipo) non sono così casuali. Ne ricordo uno rand()
di anni fa in cui il bit più basso si alternava 1
a 0
ogni chiamata a rand()
- non era molto casuale.
Dalla pagina man rand (3):
Le versioni di rand () e srand () nella libreria C di Linux usano lo stesso
generatore di numeri casuali come random () e srandom (), quindi di ordine inferiore
i bit devono essere casuali come i bit di ordine superiore. Tuttavia, su vecchi
rand () implementazioni e su implementazioni attuali su differenti
sistemi, i bit di ordine inferiore sono molto meno casuali di quelli di livello superiore
bit di ordine. Non utilizzare questa funzione in applicazioni destinate ad essere
portatile quando è necessaria una buona casualità.
Questo potrebbe essere ora relegato nella storia, ma è del tutto possibile che tu abbia ancora un'implementazione rand () scadente nascosta da qualche parte nello stack. Nel qual caso, è ancora abbastanza applicabile.
La cosa da fare è effettivamente utilizzare una buona libreria di numeri casuali (che fornisca buoni numeri casuali) e quindi chiedere numeri casuali all'interno dell'intervallo desiderato.
Un esempio di un buon numero di bit casuale di codice (dalle 13:00 nel video collegato)
#include <iostream>
#include <random>
int main() {
std::mt19937 mt(1729); // yes, this is a fixed seed
std::uniform_int_distribution<int> dist(0, 99);
for (int i = 0; i < 10000; i++) {
std::cout << dist(mt) << " ";
}
std::cout << std::endl;
}
Confronta questo con:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
srand(time(NULL));
for (int i = 0; i < 10000; i++) {
printf("%d ", rand() % 100);
}
printf("\n");
}
Esegui entrambi questi programmi e confronta la frequenza con cui determinati numeri compaiono (o non compaiono) in quell'output.
Video correlato: rand () considerato dannoso
Alcuni aspetti storici di rand () che causano bug in Nethack che si dovrebbero guardare e considerare nelle proprie implementazioni:
Problema di Nethack RNG
Rand () è una funzione fondamentale per la generazione di numeri casuali di Nethack. Il modo in cui Nethack lo utilizza è difettoso o si potrebbe sostenere che lrand48 () produce numeri pseudo-casuali scadenti. (Tuttavia, lrand48 () è una funzione di libreria che utilizza un metodo PRNG definito e qualsiasi programma che lo utilizza dovrebbe tenere conto dei punti deboli di tale metodo.)
Il bug è che Nethack si affida (a volte esclusivamente come nel caso di rn (2)) sui bit inferiori dei risultati di lrand48 (). Per questo motivo, l'RNG in tutto il gioco funziona male. Ciò è particolarmente evidente prima che le azioni dell'utente introducano ulteriore casualità, vale a dire nella generazione del personaggio e nella creazione di primo livello.
Sebbene quanto sopra fosse del 2003, dovrebbe essere tenuto presente, poiché potrebbe non essere il caso che tutti i sistemi che eseguono il gioco previsto siano un sistema Linux aggiornato con una buona funzione rand ().
Se lo stai facendo da solo, puoi testare quanto è buono il tuo generatore di numeri casuali scrivendo del codice e testando l'output con ent .
Sulle proprietà di numeri casuali
Esistono altre interpretazioni di "random" che non sono esattamente casuali. In un flusso casuale di dati, è del tutto possibile ottenere lo stesso numero due volte. Se lanci una moneta (casuale), è del tutto possibile ottenere due teste di fila. Oppure lancia due volte un dado e ottieni lo stesso numero due volte di seguito. O girare una ruota della roulette e ottenere lo stesso numero due volte lì.
La distribuzione dei numeri
Durante la riproduzione di un elenco di brani, le persone si aspettano che 'casuale' significhi che lo stesso brano o artista non verrà riprodotto una seconda volta di seguito. La riproduzione di una playlist The Beatles due volte di seguito è considerata "non casuale" (sebbene sia casuale). La percezione che per una playlist di quattro brani suonati per un totale di otto volte:
1 3 2 4 1 2 4 3
è più "casuale" di:
1 3 3 2 1 4 4 2
Altro su questo per il "mescolamento" delle canzoni: come mescolare le canzoni?
Su valori ripetuti
Se non si desidera ripetere i valori, è necessario prendere in considerazione un approccio diverso. Genera tutti i valori possibili e mescolali.
Se stai chiamando rand()
(o qualsiasi altro generatore di numeri casuali), lo stai chiamando con la sostituzione. Puoi sempre ottenere lo stesso numero due volte. Un'opzione è quella di lanciare i valori ancora e ancora fino a quando non si seleziona uno che soddisfa i requisiti. Sottolineerò che questo ha un tempo di esecuzione non deterministico ed è possibile che tu possa trovarti in una situazione in cui esiste un ciclo infinito a meno che non inizi a fare una traccia indietro più complessa.
Elenca e scegli
Un'altra opzione è quella di generare un elenco di tutti i possibili stati validi e quindi selezionare un elemento casuale da tale elenco. Trova tutti i punti vuoti (che soddisfano alcune regole) nella stanza, quindi scegline uno casuale da quell'elenco. E poi fallo ancora e ancora finché non hai finito.
rimescolare
L'altro approccio è mescolare come se fosse un mazzo di carte. Inizia con tutti i punti vuoti nella stanza e poi inizia ad assegnarli distribuendo i punti vuoti, uno alla volta, a ciascuna regola / processo che richiede un punto vuoto. Hai finito quando finisci le carte o le cose smettono di chiederle.