La pregunta es demasiado amplia para una respuesta completa, pero permítanme elegir un par de puntos interesantes:
Por qué "igualmente probable"
Suponga que tiene un generador de números aleatorios simple que genera los números 0, 1, ..., 10 cada uno con la misma probabilidad (piense en esto como el clásico rand()
). Ahora quieres un número aleatorio en el rango 0, 1, 2, cada uno con la misma probabilidad. Su reacción instintiva sería tomar rand() % 3
. Pero espere, los restos 0 y 1 ocurren con más frecuencia que el resto 2, ¡así que esto no es correcto!
Es por eso que necesitamos distribuciones adecuadas , que toman una fuente de enteros aleatorios uniformes y los convierten en nuestra distribución deseada, como Uniform[0,2]
en el ejemplo. ¡Es mejor dejar esto en una buena biblioteca!
Motores
Así, en el corazón de toda aleatoriedad hay un buen generador de números pseudoaleatorios que genera una secuencia de números que se distribuye uniformemente en un cierto intervalo, y que idealmente tiene un período muy largo. La implementación estándar de rand()
no suele ser la mejor y, por lo tanto, es bueno tener una opción. Linear-congruential y el tornado de Mersenne son dos buenas opciones (LG también se usa a menudo rand()
); de nuevo, es bueno dejar que la biblioteca se encargue de eso.
Cómo funciona
Fácil: primero, configure un motor y siembre. La semilla determina completamente la secuencia completa de números "aleatorios", así que a) use uno diferente (por ejemplo, tomado de /dev/urandom
) cada vez, yb) guarde la semilla si desea recrear una secuencia de elecciones aleatorias.
#include <random>
typedef std::mt19937 MyRNG; // the Mersenne Twister with a popular choice of parameters
uint32_t seed_val; // populate somehow
MyRNG rng; // e.g. keep one global instance (per thread)
void initialize()
{
rng.seed(seed_val);
}
Ahora podemos crear distribuciones:
std::uniform_int_distribution<uint32_t> uint_dist; // by default range [0, MAX]
std::uniform_int_distribution<uint32_t> uint_dist10(0,10); // range [0,10]
std::normal_distribution<double> normal_dist(mean, stddeviation); // N(mean, stddeviation)
... ¡Y usa el motor para crear números aleatorios!
while (true)
{
std::cout << uint_dist(rng) << " "
<< uint_dist10(rng) << " "
<< normal_dist(rng) << std::endl;
}
Concurrencia
Una razón más importante para preferir <random>
sobre lo tradicional rand()
es que ahora es muy claro y obvio cómo hacer que la generación de números aleatorios sea segura para los subprocesos: proporcione a cada subproceso con su propio motor local de subprocesos, sembrado en una semilla local de subprocesos, o sincronice el acceso al objeto motor.
Misc
- Un artículo interesante sobre TR1 aleatorio en codeguru.
- Wikipedia tiene un buen resumen (gracias, @Justin).
- En principio, cada motor debe escribir def a
result_type
, que es el tipo integral correcto para usar con la semilla. Creo que tenía una aplicación con errores una vez que me obligó a forzar la semilla para std::mt19937
que uint32_t
en x64, con el tiempo esto debe ser fijo y se puede decir MyRNG::result_type seed_val
y por lo tanto hacer que el motor muy fácilmente reemplazable.
rand
, debería echar un vistazo rápido a wikipedia para conocer algunos conceptos básicos de estadística y RNG; de lo contrario, será muy difícil explicarle la razón<random>
y el uso de sus diversas piezas.