2011-12-28 7 views
6

Me gustaría generar números aleatorios uniformes en C++ entre 0 y 1, de una manera que no utiliza el método estándar rand() y srand(time(NULL)) . La razón de esto es que si ejecuto la aplicación más de una vez dentro del mismo segundo de mi reloj, la semilla será exactamente la misma y producirá el mismo resultado.generando números aleatorios en C++ usando TR1/dev/random (resistente a <1 segundo de ejecuciones)

No deseo confiar en los detalles del compilador o el sistema operativo/compilador. x86 se puede suponer.

Parece que una forma alternativa de hacerlo es usar TR1 (no tengo C++ 11) y sembrar con /dev/random de alguna manera?

Ahora mismo tengo unas pocas cosas, pero todavía utiliza time(NULL) como una semilla que no va a funcionar bien dentro de 1 segundo corre:

#include <iostream> 
#include <tr1/random> 

int main() 
{ 
    std::tr1::mt19937 eng; 
    eng.seed(time(NULL)); 
    std::tr1::uniform_int<int> unif(1, RAND_MAX); 
    int u = unif(eng); 
    std::cout << (float)u/RAND_MAX << std::endl; 
} 
+0

¿Está dispuesto a utilizar funciones específicas de OS/compilador? – Mysticial

+0

Me gustaría evitar esto ... Lo implementaré en varios sistemas – gnychis

+0

¿Cuán variado? ¿Todavía son todos x86? – Mysticial

Respuesta

7

envío a solicitud de la OP:

Esto es aún un poco compilador específico, pero todavía funcionará en casi todos los x86 de metas de compiladores:

#ifdef _WIN32 

// Windows 
#define rdtsc __rdtsc 

#else 

// For everything else 
unsigned long long rdtsc(){ 
    unsigned int lo,hi; 
    __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); 
    return ((unsigned long long)hi << 32) | lo; 
} 

#endif 

int main() 
{ 
    std::tr1::mt19937 eng; 
    eng.seed(rdtsc()); // Seed with rdtsc. 
    std::tr1::uniform_int<int> unif(1, RAND_MAX); 
    int u = unif(eng); 
    std::cout << (float)u/RAND_MAX << std::endl; 
} 

La idea aquí es que las semillas de su azar generador de números con el contador de ciclos rdtsc.

La razón por la que esto funciona es porque el contador de ciclo rdtsc itera a aproximadamente (a menudo la misma) velocidad que la frecuencia de la CPU. Por lo tanto, las posibilidades de que dos llamadas devuelvan el mismo valor son extremadamente escasas, lo que la convierte en una excelente semilla para un RNG.

1

Su problema está relacionado con la forma en que la semilla del generador de números aleatorios. Obviamente, siembra con tiempo (NULL) va a producir la misma secuencia de PRNG dentro de ese segundo cuando se siembra. Esta es la forma más común de sembrar rand, pero desafortunadamente es una mala práctica debido a este mismo problema. No solo eso, he leído que puede causar sesgos en los resultados.

Tenga en cuenta que CADA PRNG producirá el mismo resultado si se siembra con los mismos valores. Entonces su problema no está relacionado con el generador, más con la siembra.

Hice una pregunta acerca de sembrar aquí hace unas semanas y se le dio un enlace al siguiente artículo que también puede serle útil. Good Practice in (Pseudo) Random Number Generation for Bioinformatics Applications Consulte la sección sobre la siembra o el calentamiento del generador.

rand() no es el mejor generador de números aleatorios, pero es adecuado en muchos casos, siempre que esté correctamente sembrado. Si desea algo mejor donde la secuencia de repetición es muy grande, entonces hay algunos proporcionados en ese enlace. O use los basados ​​en TR1. Personalmente, iría con más código portátil basado en C++ 03 y me mantendría alejado de TR1.

Considere también Multiply with carry como un algoritmo alternativo de PRNG.

4

TR1 en [tr.rand.device] especifica una clase random_device que genera entradas sin firmar de una fuente dependiente de la implementación. Por lo que el siguiente debería funcionar, aunque no he compilado por mí mismo:

int main() { 
    std::tr1::random_device dev_random; 
    std::tr1::mt19937 eng(dev_random()); 
    ... 

En TR1, pasando dev_random directamente sin llamar funciona e inicializa el estado del spa más al azar, pero en C++ 11 hay que envolver las semillas argumentos en otra clase.Como llamar al argumento funciona en ambas bibliotecas, lo haría para mantenerlo, a menos que tenga necesidades más exigentes.

+0

Tenga en cuenta que no hay garantía acerca de qué dispositivo aleatorio obtiene del constructor 'random_device()'. Podría ser '/ dev/random','/dev/urandom', 'rdtsc'-based, http://en.wikipedia.org/wiki/RdRand, o incluso otro pseudo-rng. Las implementaciones pueden definir un argumento de cadena para el constructor que selecciona entre las opciones, pero se supone que el valor predeterminado es decente. –

+1

random_device es un objeto de función, por lo que 'eng (dev_random())' – Cubbi

+0

@Cubbi: No: los motores tienen un constructor que toma un entero y un constructor que toma un objeto de función: "' X (g) '- crea un motor con el estado interno inicial dado por los resultados de las invocaciones sucesivas de 'g'. - [tr.rand.req] tabla 16. Esto les permite completar su estado completo en lugar de solo la primera palabra del mismo. (Por supuesto, es posible que cualquier implementación dada no haya implementado la especificación completa.) –