2010-05-18 11 views
11

Sé cómo generar un número aleatorio entre 0 y 1 utilizando el método NextDouble del generador de números pseudoaleatorios.¿Cómo generar un doble criptográficamente seguro entre 0 y 1?

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

Y sé cómo llenar una matriz de bytes aleatorios utilizando el generador de números aleatorios criptográficamente seguro.

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

Pero ¿cómo puedo convertir la salida de matriz de bytes de RNGCryptoServiceProvider en un número aleatorio uniformemente distribuido entre 0 (incluido) y 1 (exclusivo)?

+0

¿Qué distribución quieres? ¿Uniforme? – AakashM

+0

Sí, distribución uniforme. Se actualizará ahora. – Portman

Respuesta

20

Me parece que las soluciones hasta ahora tendrán una distribución desigual debido a tomar la inversa. Para una distribución pareja, creo que quieres algo como esto.

// 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); 

Tenga en cuenta que no se puede simplemente dividir el UInt64 en UInt64.MaxValue, debido a un doble no tiene suficientes bits, y no hay manera de conseguir salidas únicas para todas sus entradas. Entonces puedes/debes tirar algunas partes.

+0

Sí, creo que tienes razón. –

+0

Esto es genial, gracias. Acabo de agregar el segundo parámetro requerido a BitConverter.ToUInt64 y un elemento primario perdido en la línea 2. Probar ahora para asegurar que sea una distribución equivalente a Random.NextDouble(). – Portman

+1

Editar # 3 funciona correctamente: después de 1 millón de iteraciones, mínimo de .0000001, máximo de .999999, promedio de .5000003 después de 1 millón de iteraciones. ¿Te importa si borro el historial de edición? – Portman

1

Bueno, yo no llamaría a un número aleatorio de 64 bits "criptográficamente seguro" - querría muchos más bits que eso para estar "criptográficamente seguros". Pero de todos modos, se podría hacer algo como esto:

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

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

Es posible que desee agregar un 'Math.Abs ​​(l)' para garantizar que el doble resultante sea positivo. –

+0

@Paul: buen punto, añadido. –

+4

Esto tiene una distribución muy diferente en comparación con NextDouble. Generará números muy cercanos a cero casi todo el tiempo. –

Cuestiones relacionadas