2011-08-27 13 views
23

El nuevo estándar C++ 11 tiene un capítulo completo dedicado a generadores de números aleatorios. Pero ¿Cómo se realiza el más simple de tareas, más común que se utiliza para ser codificada como este, pero sin recurrir a la biblioteca estándar de C:¿Cómo puedo generar un número aleatorio utilizando la biblioteca estándar de C++ 11?

srand((unsigned int)time(0)); 
int i = rand();

¿Hay valores por defecto razonables para los motores de números aleatorios, distribuciones, y semillas que uno podría usar fuera de la caja?

+0

Wikipedia? http://en.wikipedia.org/wiki/C%2B%2B0x#Extensible_random_number_facility – quasiverse

+2

¿Qué problema hay con el código que tienes? AFAIK, los nuevos generadores de números aleatorios se agregaron para aplicaciones más "serias" donde los aspectos de la generación de números aleatorios realmente importan. – GManNickG

+3

@GMan: Para ser justos, varios de los motores de números aleatorios en el nuevo estándar podrían describirse como simples y rápidos y no los vería como particularmente "graves". –

Respuesta

30

Usted debe ser capaz de hacer algo como:

std::default_random_engine e((unsigned int)time(0)); 
int i = e(); 

La calidad de la default_random_engine depende de la implementación. También puede usar std::min_rand0 o std::min_rand.

Probablemente una mejor manera de inicializar un motor aleatorio es con un número aleatorio tan verdadero como está disponible en la implementación en lugar de usar time.

E.g.

std::random_device rd; 
std::default_random_engine e(rd()); 
+1

Para aclarar, ¿quiere decir "debería" como en "esto no existe pero debería" o "esto existe y su compilador debería soportarlo"? – GManNickG

+8

Informativo: la mayor diferencia entre esta respuesta y el ejemplo del OP es que el codificador controla dónde está el estado del motor: está en 'e'. En el código del OP, el estado se oculta como datos estáticos dentro de 'rand'. Esta es una diferencia importante cuando se trata de programas multiproceso. 'rand' tiene que tener algún tipo de protección para que sea seguro. 'default_random_engine' no. Si desea llamarlo desde múltiples hilos, usted mismo proporciona el mecanismo de sincronización externamente. Esto significa que 'default_random_engine' puede ser más rápido si no necesita sincronizarlo. –

+0

@GMan: Significa que la única implementación a la que tengo acceso en este momento no es compatible, por lo que no he podido probar mi código;). –

2

Si su código actual era apropiado antes del nuevo estándar, entonces seguirá siéndolo. Los nuevos generadores de números aleatorios se agregaron para aplicaciones que requieren una mayor calidad de pseudoaleatoriedad, p. simulación estocástica.

0

La generación aleatoria de números es un problema difícil. No hay una manera verdaderamente aleatoria de hacerlo. Si solo estás generando aleatoriedad para sembrar un entorno de juego, entonces tu enfoque debería estar bien. rand() tiene varias deficiencias.

Si necesita aleatoriedad para generar claves de cifrado, entonces es S.O.L. La mejor manera en ese caso es ir al sistema operativo, que generalmente tiene un mecanismo. En POSIX eso es aleatorio() (o lea desde/dev/random si está dispuesto). En Windows, puede utilizar el CryptoAPI:

https://www.securecoding.cert.org/confluence/display/seccode/MSC30-C.+Do+not+use+the+rand%28%29+function+for+generating+pseudorandom+numbers

0

Se puede usar RC4 para generar bytes aleatorios. Esto probablemente tiene las propiedades que desea. Es rápido y bastante simple de implementar. La secuencia es repetible en todas las implementaciones cuando se conoce la semilla, y es completamente impredecible cuando se desconoce la semilla. http://en.wikipedia.org/wiki/RC4

2

Uso el siguiente código en mi proyecto. 'motor' y 'distribución' pueden ser uno de los proporcionados por la biblioteca.

#include <random> 
#include <functional> 
#include <iostream> 
... 
std::uniform_int_distribution<unsigned int> unif; 
std::random_device rd; 
std::mt19937 engine(rd()); 
std::function<unsigned int()> rnd = std::bind(unif, engine); 

std::cout << rnd() << '\n'; 
6

unificar y simplificar algunas de las muestras ya previstas voy a resumir a:

// Good random seed, good engine 
auto rnd1 = std::mt19937(std::random_device{}()); 

// Good random seed, default engine 
auto rnd2 = std::default_random_engine(std::random_device{}()); 

// like rnd1, but force distribution to int32_t range 
auto rnd3 = std::bind(std::uniform_int_distribution<int32_t>{}, std::mt19937(std::random_device{}())); 

// like rnd3, but force distribution across negative numbers as well 
auto rnd4 = std::bind(std::uniform_int_distribution<int32_t>{std::numeric_limits<int32_t>::min(),std::numeric_limits<int32_t>::max()}, std::mt19937(std::random_device{}())); 

Entonces me encontré con algunas pruebas para ver cuáles son los valores por defecto se parecen:

#include <random> 
#include <functional> 
#include <limits> 
#include <iostream> 

template<class Func> 
void print_min_mean_max(Func f) { 
    typedef decltype(f()) ret_t; 
    ret_t min = std::numeric_limits<ret_t>::max(), max = std::numeric_limits<ret_t>::min(); 
    uint64_t total = 0, count = 10000000; 
    for (uint64_t i = 0; i < count; ++i) { 
     auto res = f(); 
     min = std::min(min,res); 
     max = std::max(max,res); 
     total += res; 
    } 
    std::cout << "min: " << min << " mean: " << (total/count) << " max: " << max << std::endl; 
} 

int main() { 
    auto rnd1 = std::mt19937(std::random_device{}()); 
    auto rnd2 = std::default_random_engine(std::random_device{}()); 

    auto rnd3 = std::bind(std::uniform_int_distribution<int32_t>{}, std::mt19937(std::random_device{}())); 
    auto rnd4 = std::bind(std::uniform_int_distribution<int32_t>{std::numeric_limits<int32_t>::min(),std::numeric_limits<int32_t>::max()}, std::mt19937(std::random_device{}())); 

    print_min_mean_max(rnd1); 
    print_min_mean_max(rnd2); 
    print_min_mean_max(rnd3); 
    print_min_mean_max(rnd4); 
} 

Produce la salida:

min: 234 mean: 2147328297 max: 4294966759 
min: 349 mean: 1073305503 max: 2147483423 
min: 601 mean: 1073779123 max: 2147483022 
min: -2147481965 mean: 178496 max: 2147482978 

Como podemos ver, mt19937 y default_random_engine tienen un rango predeterminado diferente, por lo que se recomienda usar uniform_int_distribution.

Además, el valor predeterminado uniform_int_distribution es [0, max_int] (no negativo), incluso cuando se utiliza un tipo de entero con signo. Debe proporcionar el rango explícitamente si desea un rango completo.

Finalmente, its important to remember this en momentos como estos.

+2

De nota, hay una versión de 64 bits de 'std :: mt19937':' std :: mt19937_64' que devuelve 64 bits de aleatoriedad por llamada. 'auto rnd5 = std :: mt19937_64 (std :: random_device {}()); // min: 4879020137534 media: 1655417118684 max: 18446741225191893648' – Sean

+0

Por cierto, ¿es seguro reutilizar el mismo motor de números aleatorios con muchas distribuciones y std :: bind() o es mejor vincular cada distribución a un nuevo instancia del motor? Cómo es esto: 'std :: mt19937_64 random = std :: mt19937_64 (std :: random_device {}());' 'auto randomCardinality = std :: bind (std :: uniform_int_distribution (1, 4), random);' 'auto randomValue = std :: bind (std :: uniform_real_distribution (-1.0, 1.0), random);' – Xharlie

2

Aquí tienes. Doble aleatorio en un rango:

// For ints 
// replace _real_ with _int_, 
// <double> with <int> and use integer constants 

#include <random> 
#include <iostream> 
#include <ctime> 
#include <algorithm> 
#include <iterator> 

int main() 
{ 
    std::default_random_engine rng(std::random_device{}()); 
    std::uniform_real_distribution<double> dist(-100, 100); //(min, max) 

    //get one 
    const double random_num = dist(rng); 

    //or.. 
    //print 10 of them, for fun. 
    std::generate_n( 
     std::ostream_iterator<double>(std::cout, "\n"), 
     10, 
     [&]{ return dist(rng);}); 
    return 0; 
} 
Cuestiones relacionadas