La domanda è troppo ampia per una risposta completa, ma permettimi di scegliere un paio di punti interessanti:
Perché "altrettanto probabile"
Supponi di avere un semplice generatore di numeri casuali che genera i numeri 0, 1, ..., 10 ciascuno con la stessa probabilità (pensa a questo come al classico rand()
). Ora vuoi un numero casuale nell'intervallo 0, 1, 2, ciascuno con la stessa probabilità. La tua reazione istintiva sarebbe quella di prendererand() % 3
. Ma aspetta, i resti 0 e 1 si verificano più spesso del resto 2, quindi questo non è corretto!
Questo è il motivo per cui abbiamo bisogno di distribuzioni appropriate , che prendono una fonte di interi casuali uniformi e li trasformano nella nostra distribuzione desiderata, comeUniform[0,2]
nell'esempio. Meglio lasciare questo a una buona libreria!
motori
Quindi al centro di tutta la casualità c'è un buon generatore di numeri pseudocasuali che genera una sequenza di numeri distribuiti uniformemente su un certo intervallo e che idealmente hanno un periodo molto lungo. L'implementazione standard di rand()
non è spesso la migliore, quindi è bene avere una scelta. Linear-congruential e il Mersenne twister sono due buone scelte (LG è in realtà spesso usato darand()
); ancora una volta, è bene lasciare che sia la libreria a gestirlo.
Come funziona
Facile: per prima cosa, imposta un motore e semina. Il seme determina completamente l'intera sequenza di numeri "casuali", quindi a) usane uno diverso (ad esempio preso da /dev/urandom
) ogni volta, eb) memorizza il seme se desideri ricreare una sequenza di scelte casuali.
#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);
}
Ora possiamo creare distribuzioni:
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)
... E usa il motore per creare numeri casuali!
while (true)
{
std::cout << uint_dist(rng) << " "
<< uint_dist10(rng) << " "
<< normal_dist(rng) << std::endl;
}
Concorrenza
Un motivo più importante per preferire <random>
il tradizionale rand()
è che ora è molto chiaro e ovvio come rendere sicura la generazione di numeri casuali: o fornire a ogni thread il proprio motore locale del thread, sezionato su un seme locale del thread o sincronizzare l'accesso all'oggetto motore.
Varie
- Un interessante articolo su TR1 random su codeguru.
- Wikipedia ha un buon riassunto (grazie, @Justin).
- In linea di principio, ogni motore dovrebbe
result_type
digitare a, che è il tipo integrale corretto da utilizzare per il seme. Penso di aver avuto un'implementazione buggy una volta che mi ha costretto a forzare il seme per std::mt19937
a uint32_t
su x64, alla fine questo dovrebbe essere risolto e si può dire MyRNG::result_type seed_val
, e quindi rendere il motore molto facilmente sostituibile.
rand
, dovresti dare una rapida occhiata a wikipedia per alcune statistiche di base e concetti RNG, altrimenti sarà davvero difficile spiegarti la logica<random>
e l'utilizzo dei suoi vari pezzi.