Perché C ++ Rand () sembra generare solo numeri dello stesso ordine di grandezza?


146

In una piccola applicazione scritta in C / C ++, sto affrontando un problema con rand funzione e forse il seme:

Voglio produrre una sequenza di numeri casuali che sono di ordini diversi, cioè con valori di logaritmo diversi (base 2). Ma sembra che tutti i numeri prodotti siano dello stesso ordine, oscillando tra 2 ^ 25 e 2 ^ 30.

È perché rand()è seminato con il tempo Unix che è ormai un numero relativamente grande? Cosa sto dimenticando? Sto seminando rand()solo una volta all'inizio del main().


7
FWIW quindi, è C o C ++? Se con C / C ++ intendi che puoi effettivamente usare C ++ e la menzione di C era solo casuale, forse questo en.cppreference.com/w/cpp/numeric/random/binomial_distribution può aiutare.
R. Martinho Fernandes,

9
Purtroppo scommettevi sul cavallo sbagliato. Il seme non dovrebbe essere il tuo problema. Il tuo problema era una distribuzione prevista errata. Poiché un programmatore imparziale si aspetterebbe rand()di restituire numeri distribuiti uniformemente (la documentazione con un alto ranking di Google lo dice esplicitamente), non penso che questa domanda sia utile per i futuri lettori. Ecco perché vota verso il basso ma non lasciarti scoraggiare dall'uso di SO.
Imperatore Orionii,

12
@ doug65536 "... dove nessun numero viene mai ripetuto" - non è casuale! Potrei finanziare la mia pensione al tavolo del craps se i miei dadi rand () non restituissero lo stesso numero due volte fino a quando non venisse restituito ogni possibile numero.
Chris Gregg,

6
@GalacticCowboy Non confondere la periodicità con una ripetizione di singoli numeri. Dall'articolo di Wikipedia che hai citato: "un risultato ripetuto non implica che sia stata raggiunta la fine del periodo, poiché il suo stato interno potrebbe essere maggiore del suo output". Sarebbe molto, molto brutto se un PRNG producesse un valore e quindi si garantisse che non lo avrebbe più prodotto fino a quando non fossero stati restituiti tutti i valori.
Chris Gregg,

12
Doug65536, nessuno sta raccogliendo combattimenti. Stanno solo affermando correttamente che ti sbagli. Un PRNG potrebbe tranquillamente sfornare quanto segue se volessi un RAND tra 1 e 10: 2 4 7 2 8 1 5 9 7 3 Sarebbe del tutto valido, nonostante i multipli 2 e 7. Penso che stai confondendo il PRNG con la funzione shuffle sul tuo iPhone.
Rilassarsi a Cipro il

Risposte:


479

Esiste solo il 3% dei numeri tra 1 e 2 30 che NON sono compresi tra 2 25 e 2 30 . Quindi, sembra abbastanza normale :)

Poiché 2 25 /2 30 = 2 -5 = 1/32 = 0,03,125 mila = 3,125%


36
Sì, buon punto! Ci sono 31 volte più numeri tra 2 ^ 25 e 2 ^ 30 che tra 1 e 2 ^ 25 :) grazie per la risposta rapida. Devo ripensare il programma allora. Domanda risposta.
Tallaron Mathias,

1
@TallaronMathias Considera di troncare il numero tramite lo >>spostamento dei bit - questo ti darà numeri più piccoli. (O prendendo un modulo con %.)
Sean Allred,

13
Mi aspetto che questo sia ovvio per la maggior parte dei programmatori: qualsiasi numero intero senza segno inferiore a 2 ^ 25 deve avere i suoi primi 7 bit uguali a 0- e se ogni bit è casuale ...
BlueRaja - Danny Pflughoeft

118
@ BlueRaja-DannyPflughoeft - se le probabilità fossero ovvie, i casinò sarebbero fuori mercato.
Brett Hale,

26
@BrettHale - Non penso che i programmatori siano demografici target di un casinò.
Ekoostik Martedì

272

Il verde più chiaro è la regione tra 0 e 2 25 ; il verde più scuro è la regione tra 2 25 e 2 30 . Le zecche sono potenze di 2.

distribuzione


42

Devi essere più preciso: vuoi valori di logaritmo di base 2 diversi ma quale distribuzione vuoi per questo? Le funzioni standard di rand () generano una distribuzione uniforme, sarà necessario trasformare questo output usando il quantile funzione associata alla distribuzione desiderata.

Se ci dici la distribuzione, allora possiamo dirti la quantilefunzione di cui hai bisogno.


13
+1, la distribuzione è il termine cruciale. Non ha molto senso parlare di numeri casuali quando non si sa nulla della distribuzione. L'uniforme è solo un caso speciale, sebbene importante. Potrebbe essere un buon posto per evidenziare varie distribuzioni dalla libreria standard C ++ 11.
leftaroundabout

18

Se vuoi diversi ordini di grandezza, perché non provarci semplicemente pow(2, rand()) ? O forse scegli direttamente l'ordine come rand (), come suggerì Harold?


3
buona idea, ma dovresti correggere la tua risposta usando pow invece di ^ (che è l'operatore logico xor, non power, in linguaggio C).
Kriss,

6
Dato che rand()può arrivare fino a RAND_MAX, devi davvero ridimensionare il tuo numero casuale in modo che il risultato non trabocchi ...
Floris,

@Floris: ma se ridimensionate un piccolo intervallo numerabile su un intervallo molto ampio, avrete MOLTI buchi, che probabilmente non è ciò che OP si aspetta.
André Caron,

13

@ C4stor ha fatto un grande punto. Ma, per un caso più generale e più facile da capire per l'uomo (base 10): per l'intervallo da 1 a 10 ^ n, ~ 90% dei numeri va da 10 ^ (n-1) a 10 ^ n, quindi, ~ 99% dei numeri va da 10 ^ (n-2) a 10 ^ n. Continua ad aggiungere tutti i decimali che desideri.

Matematica divertente, se continui a farlo per n, puoi vedere che da 1 a 10 ^ n, 99.9999 ...% = 100% dei numeri vanno da 10 ^ 0 a 10 ^ n con questo metodo.

Ora riguardo al codice, se vuoi un numero casuale con ordini di grandezza casuali, da 0 a 10 ^ n, puoi fare:

  1. Genera un piccolo numero casuale da 0 a n

  2. Se conosci l'intervallo che n ha, genera un grande numero casuale di ordine 10 ^ k dove k> max {n}.

  3. Taglia il numero casuale più lungo per ottenere le n cifre di questo grande numero casuale.


46
Hai completamente ragione, ma per una risposta VERAMENTE facile da capire, l'OP dovrebbe chiedersi perché il 90% dei numeri casuali tra 1 e 100 sono due cifre.
Chiedi a Monica il

13

La risposta di base (e corretta) era già stata data e accettata sopra: ci sono 10 numeri tra 0 e 9, 90 numeri tra 10 e 99, 900 tra 100 e 999, ecc.

Per un modo computazionalmente efficiente di ottenere una distribuzione con distribuzione approssimativamente logaritmica, si desidera spostare a destra il numero casuale di un numero casuale:

s = rand() & 31; // a random number between 0 and 31 inclusive, assuming RAND_MAX = 2^32-1
r = rand() >> s; // right shift

Non è perfetto, ma è molto più veloce dell'elaborazione pow(2, rand()*scalefactor) . Sarà "grumoso" nel senso che la distribuzione sarà uniforme per i numeri all'interno di un fattore 2 (uniforme per 128-255, metà densità per 256-1023, ecc.).

Ecco un istogramma della frequenza dei numeri da 0 a 31 (in campioni 1M):

inserisci qui la descrizione dell'immagine


nitpick: questo incoraggia numeri molto piccoli più di quanto ci si potrebbe aspettare. La probabilità di ottenere uno zero è significativamente maggiore di un 10.
Mooing Duck

Bene - il punto è di incoraggiare piccoli numeri, quindi sono contento che funzioni! Ho eseguito una simulazione Monte Carlo, e questo mi sta dando un calo del fattore 2 nella probabilità poiché i numeri raddoppiano, non diversamente da una distribuzione dei registri. Risposta aggiornata con un'immagine.
Floris,

no, voglio dire, con rand()>>(rand()&31);, ci si aspetterebbe intuitivamente 1/32 dei numeri con 32 bit e 1/32 dei numeri con 31 bit e 1/32 dei numeri con 30 bit, ecc. Ma questo è non i risultati che stai ottenendo, solo circa 1/64 del numero comporterebbe 32 bit, mentre quasi la metà dovrebbe essere 0. Dato che la mia matematica mentale non è d'accordo con le tue misurazioni, dovrò fare le mie misurazioni per capire questo fuori.
Mooing Duck il

2
Non intendo dire che il tuo codice è sbagliato. Probabilmente è quello che vorrei fare. Merita solo un avvertimento che i risultati non sono abbastanza distribuiti come ci si potrebbe aspettare.
Mooing Duck,

1
Penso che il problema derivi dal pensare a 0 come un numero di 1 bit ... questo è il tipo di enigma che incontri quando mescoli numeri interi e logaritmi. È stato comunque un buon esercizio e mi hai dato qualcosa a cui pensare. "Metti alla prova i limiti del tuo algoritmo": non invecchia mai.
Floris,

5

Esiste un numero esattamente uguale di numeri tra 0 e 2 ^ 29 e 2 ^ 29 e 2 ^ 30.

Un altro modo di vedere il problema: considera la rappresentazione binaria del numero casuale che generi, la probabilità che il bit più alto sia 1 uguale a 1/2 e, quindi, ottieni l'ordine 29 in mezzo caso. Quello che vuoi è vedere un numero inferiore a 2 ^ 25, ma ciò significa che 5 bit più alti sono tutti zero, il che accade con una bassa probabilità di 1/32. È probabile che anche se lo esegui per molto tempo, non vedrai mai l'ordine inferiore a 15 (la probabilità è qualcosa di simile a rotolare 6 6 volte di seguito).

Ora, la parte della tua domanda sul seme. No, il seme non può determinare l'intervallo da cui vengono generati i numeri, determina solo il primo elemento iniziale. Pensa a rand () come una sequenza di tutti i possibili numeri nell'intervallo (permutazione predeterminata). Il seme determina da dove iniziare a disegnare numeri dalla sequenza. Questo è il motivo per cui se vuoi la (pseudo) casualità, usi il tempo corrente per inizializzare la sequenza: non ti importa che la posizione da cui inizi non sia distribuita uniformemente, tutto ciò che conta è che non inizi mai dalla stessa posizione.


2

usarlo pow(2,rand()) darà le risposte in ordine di grandezza desiderato !!


2

Se vuoi usare numeri casuali da un servizio online che puoi usare wget per questo, potresti voler vedere che puoi anche usare servizi come random.org per la tua generazione di numeri casuali, puoi prenderli usando wget e quindi leggere i numeri da il file scaricato

wget -q https://www.random.org/integers/?num=100&min=1&max=100&col=5&base=10&format=html&rnd=new -O new.txt

http://programmingconsole.blogspot.in/2013/11/a-better-and-different-way-to-generate.html


Benvenuti in SO. astenersi dal pubblicare link come risposte. È possibile fornire uno schizzo dettagliato di una risposta lasciando i dettagli da leggere tramite collegamenti.
Shai,
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.