Pregunta

Me gustaría obtener distribución uniforme en el rango [0.0, 1.0)

Si es posible, permita que la implementación use bytes aleatorios de /dev/urandom.

También sería bueno si su solución fuera segura para subprocesos . Si no está seguro, indíquelo.

Consulte alguna solución en la que pensé después de leer otras respuestas.

¿Fue útil?

Solución 2

Esto parece ser una buena manera:

unsigned short int r1, r2, r3;
// let r1, r2 and r3 hold random values
double result = ldexp(r1, -48) + ldexp(r2, -32) + ldexp(r3, -16);

Esto se basa en la implementación drand48 de NetBSD.

Otros consejos

Simple : un doble tiene 52 bits de precisión al asumir IEEE. Así que genere un entero aleatorio sin signo de 52 bits (o más) (por ejemplo, leyendo bytes de dev / urandom), conviértalo en un doble y divídalo por 2 ^ (número de bits que fue).

Esto proporciona una distribución numéricamente uniforme (en la que la probabilidad de que un valor esté en un rango dado es proporcional al rango) hasta el 52 dígito binario.

Complicado : Sin embargo, hay muchos valores dobles en el rango [0,1) que lo anterior no puede generar. Para ser específicos, la mitad de los valores en el rango [0,0.5) (los que tienen su conjunto de bits menos significativo) no pueden ocurrir. Tres cuartos de los valores en el rango [0,0.25) (los que tienen uno de sus 2 conjuntos de bits mínimos establecidos) no pueden ocurrir, etc., hasta que solo sea posible un valor positivo inferior a 2 ^ -51, A pesar de ser un doble capaz de representar a los squillones de tales valores. Por lo tanto, no se puede decir que sea realmente uniforme en todo el rango especificado a la máxima precisión.

Por supuesto, no queremos elegir uno de esos dobles con la misma probabilidad, porque entonces el número resultante será, en promedio, demasiado pequeño. Todavía necesitamos que la probabilidad de que el resultado esté en un rango dado sea proporcional al rango, pero con una mayor precisión en qué rangos funciona.

Yo pienso los siguientes trabajos. No he estudiado ni probado particularmente este algoritmo (como probablemente se puede decir por la forma en que no hay código), y personalmente no lo usaría sin encontrar las referencias adecuadas que indiquen que es válido. Pero aquí va:

  • Inicia el exponente a 52 y elige un entero sin signo aleatorio de 52 bits (asumiendo 52 bits de mantisa).
  • Si el bit más significativo del entero es 0, aumente el exponente en uno, desplace el entero a la izquierda y complete el bit menos significativo con un nuevo bit aleatorio.
  • Repite hasta que alcances un 1 en el lugar más significativo, o el exponente se vuelve demasiado grande para tu doble (1023. O posiblemente 1022).
  • Si encuentra un 1, divida su valor entre 2 ^ exponente. Si obtuviste todos los ceros, devuelve 0 (lo sé, eso no es realmente un caso especial, pero hace hincapié en lo poco probable que sea una devolución de 0 [Editar: en realidad podría ser un caso especial, depende de si quieres generar o no Si no es así, una vez que tenga suficientes 0 en una fila, descarte todo lo que quede y devuelva 0. Pero en la práctica esto es tan poco probable que sea insignificante, a menos que la fuente aleatoria no sea aleatoria).

No sé si realmente hay algún uso práctico para un doble tan aleatorio, claro. Su definición de aleatoriedad debe depender hasta cierto punto para lo que sea. Pero si puede beneficiarse de que los 52 bits significativos sean aleatorios, esto podría ser útil.

La lectura de archivos es AFAIK segura para subprocesos, por lo que usar fopen () para leer desde / dev / urandom producirá " verdaderamente aleatorio " bytes.

Aunque puede haber posibles errores, el método cree que cualquier conjunto de dichos bytes a los que se accede como un entero, dividido por el entero máximo de ese tamaño, generará un valor de punto flotante entre 0 y 1 con aproximadamente esa distribución.

Por ejemplo:

FILE* f = fopen("/dev/urandom", "r");
int32_t int;
fread(&int, sizeof(int32_t), 1, f);
fclose(f);
double theRandomValue = int / (double) (2 ** 32 - 1);

El truco es que necesitas un aleatorizador de 54 bits que cumpla con tus requisitos. Unas pocas líneas de código con una unión para pegar esos 54 bits en la mantisa y tienes tu número. El truco no es hacer doble flotar, el truco es su aleatorizador deseado.

#include <stdlib.h>
printf("%f\n", drand48());

/ dev / random:

double c;
fd = open("/dev/random", O_RDONLY);
unsigned int a, b;
read(fd, &a, sizeof(a));
read(fd, &b, sizeof(b));
if (a > b)
   c = fabs((double)b / (double)a);
else
    c = fabs((double)a / (double)b);

c es tu valor aleatorio

/ dev / urandom no es POSIX, y generalmente no está disponible.

La forma estándar de generar un doble uniformemente en [0,1) es generar un número entero en el rango [0,2 ^ N) y dividir por 2 ^ N. Así que elige tu generador de números aleatorios favorito y úsalo. Para las simulaciones, el mío es el Mersenne Twister , ya que es extremadamente rápido, pero todavía no está bien correlacionado . En realidad, puede hacer esto por usted, e incluso tiene una versión que le dará más precisión para los números más pequeños. Normalmente, para empezar, le das una semilla, lo que ayuda a la repetibilidad para depurar o mostrar a otros tus resultados. Por supuesto, puede hacer que su código capture un número aleatorio de / dev / urandom como semilla si no se especifica uno.

Para propósitos criptográficos, debe usar una de las bibliotecas criptográficas estándar, como openssl ) que de hecho usará / dev / urandom cuando esté disponible.

En cuanto a la seguridad de los subprocesos, la mayoría no lo será, al menos con las interfaces estándar, por lo que tendrá que construir una capa en la parte superior, o solo usarlos en un solo subproceso. Los que están a salvo de subprocesos le han proporcionado un estado que modifica, de modo que en su lugar está ejecutando efectivamente múltiples generadores de números aleatorios que no interactúan, lo que puede que no sea lo que está buscando.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top