2010-05-18 1 views
11

So come generare un numero casuale compreso tra 0 e 1 utilizzando il metodo NextDouble del generatore di numeri pseudo-casuali.Come generare un doppio crittograficamente sicuro tra 0 e 1?

var rng1 = new System.Random(); 
var random1 = rng1.NextDouble(); // generates a random double between 0 and 1.0 

E so come riempire un array di byte casuali utilizzando il generatore di numeri casuali crittograficamente sicuro.

Byte[] bytes = new Byte[8]; 
var rng2 = new System.Security.Cryptography.RNGCryptoServiceProvider(); 
rng2.GetBytes(bytes); // generates 8 random bytes 

Ma come posso convertire l'uscita di byte-array di RNGCryptoServiceProvider in un numero casuale uniformemente distribuito tra 0 (incluso) e 1 (esclusiva)?

+0

Quale distribuzione desideri? Uniforme? – AakashM

+0

Sì, distribuzione uniforme. Si aggiornerà ora. – Portman

risposta

20

mi sembra che le soluzioni finora avranno distribuzione non uniforme a causa di prendere l'inverso. Per una distribuzione uniforme, penserei che tu voglia qualcosa di simile.

// Step 1: fill an array with 8 random bytes 
var rng = new RNGCryptoServiceProvider(); 
var bytes = new Byte[8]; 
rng.GetBytes(bytes); 
// Step 2: bit-shift 11 and 53 based on double's mantissa bits 
var ul = BitConverter.ToUInt64(bytes, 0)/(1 << 11); 
Double d = ul/(Double)(1UL << 53); 

Si noti che non si può semplicemente dividere l'UInt64 in UInt64.MaxValue, perché un doppio non ha abbastanza bit, e non c'è modo per ottenere risultati unici per tutti gli ingressi. Quindi puoi/devi buttare via alcuni pezzi.

+0

Sì, penso che tu abbia ragione. –

+0

Questo è grandioso, grazie. Ho appena aggiunto il secondo parametro richiesto a BitConverter.ToUInt64 e un genitore mancante sulla riga 2. Esegui ora il test per assicurarti che sia una distribuzione equivalente a Random.NextDouble(). – Portman

+1

La modifica n. 3 funziona bene: dopo 1 milione di iterazioni, minimo di .0000001, massimo di .999999, media di 500.0003 dopo 1 milione di iterazioni. Ti dispiace se pulisco la cronologia delle modifiche? – Portman

1

Beh, non definirei un numero casuale a 64 bit "crittograficamente sicuro" - vorresti molto più bit di quello che sia "crittograficamente sicuro". Ma in ogni caso, si potrebbe fare qualcosa di simile:

var bytes = // assume this contains 8 bytes of random numbers 

long l = BitConverter.ToInt64(bytes); 
double d = Math.Abs(1/(double)l); 
+0

Potrebbe voler aggiungere un 'Math.Abs ​​(l)' per garantire che il doppio risultante sia positivo. –

+0

@Paul: buon punto, aggiunto. –

+4

Ha una distribuzione molto diversa rispetto a NextDouble. Genererà numeri praticamente vicini allo zero quasi sempre. –