web-dev-qa-db-fra.com

Comment générer un nombre aléatoire à l'aide de la bibliothèque standard C++ 11

La nouvelle norme C++ 11 comprend un chapitre entier consacré aux générateurs de nombres aléatoires. Mais comment puis-je exécuter la tâche la plus simple et la plus courante qui était codée de cette manière, mais sans recourir à la bibliothèque C standard:

srand ((unsigned int) time (0)); 
 int i = Rand ();

Existe-t-il des valeurs par défaut raisonnables pour les moteurs de nombres aléatoires, les distributions et les valeurs de départ pouvant être utilisées par défaut?

23
Bartosz Milewski

Vous devriez pouvoir faire quelque chose comme:

std::default_random_engine e((unsigned int)time(0));
int i = e();

La qualité du default_random_engine dépend de la mise en œuvre. Vous pouvez également utiliser std::min_Rand0 ou std::min_Rand.

Un meilleur moyen de générer un moteur aléatoire consiste probablement à utiliser un nombre aléatoire aussi vrai que celui disponible dans l'implémentation plutôt que d'utiliser time.

Par exemple.

std::random_device rd;
std::default_random_engine e( rd() );
30
CB Bailey

En unifiant et en simplifiant certains des échantillons déjà fournis, je résumerai les points suivants:

// Good random seed, good engine
auto rnd1 = std::mt19937(std::random_device{}());

// Good random seed, default engine
auto rnd2 = std::default_random_engine(std::random_device{}());

// like rnd1, but force distribution to int32_t range
auto rnd3 = std::bind(std::uniform_int_distribution<int32_t>{}, std::mt19937(std::random_device{}()));

// like rnd3, but force distribution across negative numbers as well
auto rnd4 = std::bind(std::uniform_int_distribution<int32_t>{std::numeric_limits<int32_t>::min(),std::numeric_limits<int32_t>::max()}, std::mt19937(std::random_device{}()));

Ensuite, j'ai fait quelques tests pour voir à quoi ressemblent les valeurs par défaut:

#include <random>
#include <functional>
#include <limits>
#include <iostream>

template<class Func>
void print_min_mean_max(Func f) {
   typedef decltype(f()) ret_t;
   ret_t min = std::numeric_limits<ret_t>::max(), max = std::numeric_limits<ret_t>::min();
   uint64_t total = 0, count = 10000000;
   for (uint64_t i = 0; i < count; ++i) {
      auto res = f();
      min = std::min(min,res);
      max = std::max(max,res);
      total += res;
   }
   std::cout << "min: " << min << " mean: " << (total/count) << " max: " << max << std::endl;
}

int main() {
   auto rnd1 = std::mt19937(std::random_device{}());
   auto rnd2 = std::default_random_engine(std::random_device{}());

   auto rnd3 = std::bind(std::uniform_int_distribution<int32_t>{}, std::mt19937(std::random_device{}()));
   auto rnd4 = std::bind(std::uniform_int_distribution<int32_t>{std::numeric_limits<int32_t>::min(),std::numeric_limits<int32_t>::max()}, std::mt19937(std::random_device{}()));

   print_min_mean_max(rnd1);
   print_min_mean_max(rnd2);
   print_min_mean_max(rnd3);
   print_min_mean_max(rnd4);
}

Produit la sortie:

min: 234 mean: 2147328297 max: 4294966759
min: 349 mean: 1073305503 max: 2147483423
min: 601 mean: 1073779123 max: 2147483022
min: -2147481965 mean: 178496 max: 2147482978

Comme nous pouvons le constater, mt19937 et default_random_engine ont une plage par défaut différente. Il est donc conseillé d’utiliser uniform_int_distribution.

En outre, uniform_int_distribution par défaut est [0, max_int] (non négatif), même si vous utilisez un type entier signé. Doit fournir explicitement la gamme si vous voulez la gamme complète.

Enfin, il est important de se rappeler cela dans des moments comme ceux-ci.

6
mmocny

J'utilise le code suivant dans mon projet. 'moteur' et 'distribution' peuvent être ceux fournis par la bibliothèque.

#include <random>
#include <functional>
#include <iostream>
...
std::uniform_int_distribution<unsigned int> unif;
std::random_device rd;
std::mt19937 engine(rd());
std::function<unsigned int()> rnd = std::bind(unif, engine);

std::cout << rnd() << '\n';
2
dimitri

Voici. Doubles au hasard dans une plage:

// For ints
// replace _real_ with _int_, 
// <double> with <int> and use integer constants

#include <random>
#include <iostream>
#include <ctime>
#include <algorithm>
#include <iterator>

int main()
{
    std::default_random_engine rng(std::random_device{}()); 
    std::uniform_real_distribution<double> dist(-100, 100);  //(min, max)

    //get one
    const double random_num = dist(rng);

    //or..
    //print 10 of them, for fun.
    std::generate_n( 
        std::ostream_iterator<double>(std::cout, "\n"), 
        10, 
        [&]{ return dist(rng);} ); 
    return 0;
}
2
Carl

Si votre code existant était approprié avant la nouvelle norme, il le restera. Les nouveaux générateurs de nombres aléatoires ont été ajoutés pour les applications nécessitant une qualité pseudo-aléatoire plus élevée, par ex. simulation stochastique.

2
David Heffernan

Vous pouvez utiliser RC4 pour générer des octets aléatoires. Cela a probablement les propriétés que vous souhaitez. C'est rapide et assez simple à mettre en œuvre. La séquence est répétable dans toutes les implémentations lorsque la graine est connue et complètement imprévisible lorsque la graine est inconnue. http://en.wikipedia.org/wiki/RC4

0
Matt Mahoney