web-dev-qa-db-fra.com

Génération de nombres aléatoires en C ++ 11: comment générer, comment ça marche?

Je suis récemment tombé sur un nouveau moyen de générer des nombres aléatoires en C++ 11, mais je ne pouvais pas digérer le moteur papers que j'ai lu (quel est ce moteur , terme mathématique tel que distribution , "où tous les entiers produits ont la même probabilité ").

Alors quelqu'un peut-il expliquer

  • que sont-ils?
  • que veulent-ils dire?
  • comment générer?
  • comment travaillent-ils?
  • etc

Vous pouvez appeler tout cela en un FAQ concernant la génération de nombres aléatoires.

88
user72424

La question est trop large pour une réponse complète, mais laissez-moi sélectionner quelques points intéressants:

Pourquoi "tout aussi probable"

Supposons que vous disposiez d'un simple générateur de nombres aléatoires générant les nombres 0, 1, ..., 10 avec une probabilité égale (pensez à cela comme à la classique Rand()). Vous voulez maintenant un nombre aléatoire compris entre 0, 1 et 2, avec une probabilité égale. Votre réaction réflexe serait de prendre Rand() % 3. Mais attendez, les restes 0 et 1 sont plus fréquents que les 2 restants, ce n'est donc pas correct!

C'est pourquoi nous avons besoin de bonnes distributions , qui prennent une source d'entiers aléatoires uniformes et les transforment en notre distribution souhaitée, comme Uniform[0,2] Dans l'exemple. Le mieux est de 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, idéalement, ont 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 un choix. Linear-congruential et le twister de Mersenne sont deux bons choix (LG est en fait souvent utilisé par Rand(), aussi); encore une fois, il est bon de laisser la bibliothèque gérer cela.

Comment ça marche

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 une valeur différente (p. Ex. Tirée de /dev/urandom) À chaque fois, et b) enregistrez 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;

}

Simultanéité

Une autre raison importante de préférer <random> À la Rand() traditionnelle est qu'il est maintenant très clair et évident de rendre la génération de nombres aléatoires threadsafe: Soit fournir à chaque thread son propre thread engine, créé sur une graine locale au thread, ou synchronisez l'accès à l'objet de moteur.

Misc

  • Un article intéressant sur TR1 aléatoire sur codeguru.
  • Wikipedia a un bon résumé (merci, @Justin).
  • En principe, chaque moteur devrait typedef un result_type, Qui est le type intégral correct à utiliser pour la graine. Je pense que j’ai eu une implémentation buggy qui m’a forcé à forcer la graine de std::mt19937 À uint32_t Sur x64, cela devrait finalement être corrigé et vous pouvez dire MyRNG::result_type seed_val Et ainsi le moteur est très facilement remplaçable.
125
Kerrek SB

Un générateur de nombres aléatoires est une équation qui, à partir d’un nombre, vous donnera un nouveau nombre. Généralement, vous fournissez le premier numéro ou son numéro extrait de l'heure du système.

Chaque fois que vous demandez un nouveau numéro, il utilise le numéro précédent pour exécuter l'équation.

Un générateur de nombres aléatoires n’est pas considéré comme très bon s’il a tendance à produire le même nombre plus souvent que les autres. c'est-à-dire si vous vouliez un nombre aléatoire compris entre 1 et 5 et que vous disposiez de cette distribution de nombres:

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

2 est généré FAR plus souvent que n’importe quel autre nombre, il est donc plus susceptible d’être produit que d’autres. Si tous les chiffres étaient identiques, vous auriez 20% de chances d'obtenir chaque nombre à chaque fois. Pour le dire autrement, la distribution ci-dessus est très inégale parce que 2 est favorisée. Une distribution avec tous les 20% serait pair.

Généralement, si vous voulez un nombre aléatoire réel, vous devez extraire des données de conditions météorologiques ou d'une autre source naturelle plutôt que d'un générateur de nombres aléatoires.

2
mydogisbox