Generación de números aleatorios en C ++ 11: ¿cómo generar, cómo funciona? [cerrado]

102

Recientemente encontré una nueva forma de generar números aleatorios en C ++ 11, pero no pude digerir los artículos que leí al respecto (qué es ese motor , término matemático como distribución , "donde todos los enteros producidos son igualmente probables ").

Entonces, ¿alguien puede explicarme?

  • ¿Qué son?
  • que significan
  • como generar
  • ¿Cómo trabajan?
  • etc

Puede llamarlo todo en una pregunta frecuente sobre la generación de números aleatorios.

informatik01
fuente
6
Preguntar sobre RNG sin saber qué es una distribución es como preguntar sobre analizadores de expresión sin saber qué es una expresión ... La biblioteca RNG en C ++ 11 está dirigida a personas que conocen algunas estadísticas y tienen mayores necesidades que la distribución plana generada por 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.
Matteo Italia
26
@Matteo: Difícilmente. Un niño puede captar el concepto de que un dado produce números aleatorios, sin entender qué es una distribución.
Benjamin Lindley
3
@Benjamin: y ahí es donde se detiene su comprensión, que es solo el primer paso (los motores), y sin siquiera entender por qué es importante que generen una distribución plana. Todo el resto de la biblioteca sigue siendo un misterio sin comprender las distribuciones y otros conceptos estadísticos.
Matteo Italia

Respuestas:

142

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::mt19937que uint32_ten x64, con el tiempo esto debe ser fijo y se puede decir MyRNG::result_type seed_valy por lo tanto hacer que el motor muy fácilmente reemplazable.
Kerrek SB
fuente
Una vez más, Kerrek me gana con una respuesta mucho mejor que en la que estaba trabajando. +1
Justin ᚅᚔᚈᚄᚒᚔ
@Justin: Estoy seguro de que me he perdido un montón de cosas, ¡siéntete libre de agregar más aspectos a este tema! :-)
Kerrek SB
13
Para la parte de "poblar de alguna manera", creo que std::random_devicevale la pena mencionarlo en lugar de/dev/urandom
Cubbi
2
std::random_devicePuede encontrar un ejemplo de aquí .
WKS
1
El código del artículo de Wikipedia tiene errores. random y random2 son idénticos. A partir de los comentarios en el fragmento de código, queda claro que el autor no entiende cómo usar las funciones en <random>.
user515430
3

Un generador de números aleatorios es una ecuación que, dado un número, le dará un número nuevo. Por lo general, proporciona el primer número o se extrae de algo como la hora del sistema.

Cada vez que solicita un nuevo número, utiliza el número anterior para ejecutar la ecuación.

Un generador de números aleatorios no se considera muy bueno si tiende a producir el mismo número con más frecuencia que otros números. es decir, si quisiera un número aleatorio entre uno y 5 y tuviera esta distribución de números:

  • 1: 1%
  • 2: 80%
  • 3: 5%
  • 4: 5%
  • 5: 9%

2 se genera MUCHO más a menudo que cualquier otro número, por lo que es más probable que se produzca que otros números. Si todos los números fueran iguales, tendría un 20% de posibilidades de obtener cada número cada vez. Para decirlo de otra manera, la distribución anterior es muy desigual porque se favorece el 2. Una distribución con todos los del 20% sería uniforme.

Por lo general, si desea un número aleatorio verdadero, extraerá datos de algo como el clima o alguna otra fuente natural en lugar de un generador de números aleatorios.

N / A
fuente
8
La mayoría de los generadores de números aleatorios generan buenas distribuciones uniformes. Simplemente no son aleatorios; el problema es que están calculados y, por lo tanto, puede adivinar el siguiente número dado un número suficiente en la secuencia (este hecho los hace malos para la seguridad cuando se requieren números verdaderamente aleatorios). Para juegos y esas cosas, deberías estar bien.
Martin York
5
Estoy bastante seguro de que el OP está solicitando información específica sobre las funciones proporcionadas en el encabezado C ++ <random>. Esta respuesta ni siquiera aborda la programación y mucho menos C ++.
Benjamin Lindley
1
@Martin: La seguridad no requiere necesariamente una fuente de números verdaderamente aleatorios. AES en modo contador (por ejemplo) puede funcionar bastante bien a pesar de que es determinista. Requiere una cantidad razonable de entropía en la clave, pero no una verdadera aleatoriedad.
Jerry Coffin
@Benjamin Lindley: No importa. Simplemente releí y me di cuenta de que estaba equivocado.
N_A