La question est beaucoup trop large pour une réponse complète, mais permettez-moi de choisir quelques points intéressants:
Pourquoi "tout aussi probable"
Supposons que vous ayez un générateur de nombres aléatoires simple qui génère les nombres 0, 1, ..., 10 chacun avec une probabilité égale (pensez à cela comme au classique rand()
). Vous voulez maintenant un nombre aléatoire dans la plage 0, 1, 2, chacun avec une probabilité égale. Votre réaction instinctive serait à prendre rand() % 3
. Mais attendez, les restes 0 et 1 se produisent plus souvent que le reste 2, donc ce n'est pas correct!
C'est pourquoi nous avons besoin de distributions appropriées , qui prennent une source d'entiers aléatoires uniformes et les transforment en notre distribution souhaitée, comme Uniform[0,2]
dans l'exemple. Mieux vaut laisser cela à une bonne bibliothèque!
Moteurs
Ainsi, au cœur de tout caractère aléatoire se trouve un bon générateur de nombres pseudo-aléatoires qui génère une séquence de nombres uniformément répartis sur un certain intervalle, et qui ont idéalement une très longue période. L'implémentation standard de rand()
n'est pas souvent la meilleure et il est donc bon d'avoir le choix. Linear-congruential et le twister de Mersenne sont deux bons choix (LG est également souvent utilisé par rand()
); encore une fois, il est bon de laisser la bibliothèque gérer cela.
Comment ça fonctionne
Facile: commencez par installer un moteur et semez-le. La graine détermine entièrement la séquence entière de nombres "aléatoires", donc a) utilisez-en une différente (par exemple prise de /dev/urandom
) à chaque fois, et b) stockez la graine si vous souhaitez recréer une séquence de choix aléatoires.
#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);
}
Maintenant, nous pouvons créer des distributions:
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)
... Et utilisez le moteur pour créer des nombres aléatoires!
while (true)
{
std::cout << uint_dist(rng) << " "
<< uint_dist10(rng) << " "
<< normal_dist(rng) << std::endl;
}
Concurrence
Une autre raison importante à préférer <random>
au traditionnel rand()
est qu'il est maintenant très clair et évident de rendre la génération de nombres aléatoires sûre pour les threads: soit fournir à chaque thread son propre moteur thread-local, amorcé sur une graine locale au thread, soit synchroniser l'accès à l'objet moteur.
Divers
- Un article intéressant sur TR1 random sur codeguru.
- Wikipedia a un bon résumé (merci, @Justin).
- En principe, chaque moteur doit typedef a
result_type
, qui est le type d'intégrale correct à utiliser pour la graine. Je pense que j'ai eu une implémentation buggy une fois qui m'a forcé à forcer la graine pour std::mt19937
to uint32_t
sur x64, finalement cela devrait être corrigé et vous pouvez dire MyRNG::result_type seed_val
et ainsi rendre le moteur très facilement remplaçable.
rand
, vous devriez jeter un coup d'œil sur wikipedia pour quelques statistiques de base et des concepts RNG, sinon il sera vraiment difficile de vous expliquer la raison d'être<random>
et l'utilisation de ses différents éléments.