Les fonctions aléatoires de la bibliothèque random en C++ 11

Fabien Brissonneau
Fabien Brissonneau 1 commentaire 2e lecture en min

La librairie standard a été étoffée avec l’arrivée de la version 11 du C++. Pour la gestion des fonctions aléatoires, nous avons aujourd’hui à disposition une bibliothèque assez complète qui suit bien mieux les standards C++ que le bon vieux rand(). Dans cet article, je vous présente sans prétention les alternatives à rand() et consort, telles qu’elles devraient être implémentées. Si votre compilateur ou environnement ne supporte pas C++11 complètement, et c’est tout à fait possible, les codes suivants ne compileront pas.

Un tirage aléatoire avec rand

Pour utiliser rand(), il faut disposer de la bibliothèque cstdlib : #include <cstdlib>

				
					for   (int  i  =  0;  i   <  10;  i++)

{

           int  valeur = rand()

          std::cout<< "valeur rand" << valeur << std::endl;

}
				
			

Pour un résultat :

Nous remarquons tout d’abord que les tirages aléatoires sont toujours les mêmes. Ensuite que nous n’avons pas borné le tirage.

Pour obtenir des tirages qui sont différents à chaque lancement, il faut initialiser la série aléatoire :

				
					srand((unsigned int)time(0));

for  (int  i  =  0;  i  <  10;  i++)

{

        int valeur  = rand();

       std:: cout   << "valeur rand "  << valeur <<std::endl;

}
				
			

L’utilisation de time ici permet d’initialiser la série avec une graine qui dépend du temps, donc variable avec les exécutions. Ne pas oublier l’include #include <ctime>. Le transtypage n’est nécessaire que pour faire taire le compilateur qui se plaint sur les types unsigned int et time_t. Le type time_t est sur mon compilateur compatible avec unsigned int, mais ce n’est pas garanti par la norme.

Enfin, pour obtenir des valeurs entre 1 et 6 (comme pour un dé classique), je rajoute le code suivant :

				
					srend((unsigned int)time(0));

for   (int  i  =  0;  i   <  10;  i++)

{

        int valeur  = rand()%6   +1;

      std::cout  <<  "valeur  rand "  <<  valeur  <<  std::endl;

}
				
			

Les valeurs vont de 1 à 6 inclus. Le tirage n’est pas excellent en terme d’aléatoire, il serait sans doute meilleur d’utiliser RAND_MAX pour calculer la valeur entre 1 et 6.

 

Un tirage aléatoire avec la bibliothèque random

Utilisant la dernière version de C++, vous pouvez mettre en œuvre la biliothèque random.

En utlisant #include <random>, vous accédez aux classes de cette bibliothèque.

Il y a essentiellement 2 catégories de classes indispensables : les moteurs de génération aléatoire et les distributions. Le moteur de génération, comme son nom l’indique, produit des séries de nombres pseudo-aléatoires (utilisant une graine à fournir), et les distributions accommodent ces séries suivant les lois classiques : uniformes, de Bernoulli, de Poisson ou normales.

Le choix du moteur de génération se fait en fonction de considérations de rapidité et d’occupation mémoire. Les algorithmes proposés par la norme sont : linear_congruential_engine, mersenne_twister_engine et subtract_with_carry_engine. Ces classes sont des template, donc paramétrables. Il y a aussi des adaptateurs de moteur de génération, qui utilisent les moteurs décrits ci-dessus, mais qui altèrent certaines caractéristiques.

Enfin, des générateurs classiques, basés sur les moteurs ci-dessus, sont proposés. Ce sont ces moteurs là que la plupart d’entre nous vont utiliser. Pour la définition exacte des algorithmes utilisés, je vous laisse vous reporter à la documentation : minstd_rand0, minstd_rand, mt19937, mt19937_64, ranlux24_base, ranlux48_base, ranlux24, ranlux48, knuth_b et default_random_engine. Ce dernier est dépendant de l’implémentation de votre bibliothèque.

Pour faire un tirage, il faut choisir un moteur (ou bien paramétrer le vôtre) et choisir une distribution. Le code suivant permet par exemple un tirage entre 1 et 6, suivant une distribution équiprobable entre ces bornes.

				
					std :: default_random_engine  re;

std::uniform_int_distribution<int> distrib{  1.  6  };

for  (int i  =  0;  i  <  12;  i++)  {

        std:: cout << distrib(re)  <<  std::endl;

}
				
			

Nous retrouvons dans le cas le même problème d’initialisation de la série aléatoire que précédemment avec rand(). Pour initialiser la série, il faut fournir un paramètre au moteur de génération. Par exemple le temps.

				
					std:: default_random_engine  re(time(0));

std::uniform_int_distribution<int> distrib{  1,  6  };

for  (int  i  =  0;  i  <  12;  i++)  {

        std::cout  <<  distrib(re)  << std::endl;

}
				
			

Si vous voulez le faire avec la bibliothèque chrono (standard C++11), voici le code à écrire, en n’oubliant pas l’include : #include<chrono>

				
					std::default_random_engine re(std::chrono::system_clock::now().time_since_epoch().count());

std:uniform_int_distribution<int> distrib{  1,  6  };

for  (int  i  =  0;  i  <  12;i++)  {

           std::cout  << distrib(re)  <<  std::endl;

}
				
			

Enfin, pour simplifier les tirages, on peut utiliser bind (standard). Voici le code à écrire, ne pas oublier l’include #include <functional>. Ce code n’apporte rien au tirage lui-même, mais peut vous simplifier la vie pour les appels.

				
					std::default_random_engine re(std::chrono::system_clock::now().time_since_epoch().count());

std::uniforme_int_distribution<int> distrib{1,   6    };

auto rd =  bind(distrib,  re);

for    (int    i=    0;   i    <   12;i++)     {

          std::cout    <<    rd()  <<  std::endl;
          
          }
				
			

Finissons avec une implémentation d’une classe Dé à 6 faces.

				
					]class   De   {

   public  :

            std::default_random_engine   re;

           std::uniform_int_distribution<int>  distrib{  1,   6   };

 ]       De()

          :re(std::chrono::system_clock::now().time_since_epoch().count())  {

          }

]       int rouler()    {

                    return distib(re);

           }

};
				
			

Faites rouler le dé et vous obtenez un tirage aléatoire 😉

Conclusion

D’un façon générale, la bibliothèque random du C++11 est bien meilleure en terme d’aléatoire que la bonne vieille fonction rand(). Mais elle propose surtout beaucoup plus de possibilités de paramétrage. En utilisant ce qui est déjà configuré en terme de moteur et de distribution, vous avez de quoi couvrir pas mal de besoins. Il est conseillé, sauf si vous avez réellement des besoins spécifiques, d’utiliser le moteur pas défaut.

En Résumé :

  1. Nouveautés en C++11 : Introduction aux avancées de C++11, en particulier pour la génération aléatoire.
  2. Bibliothèque de Génération Aléatoire : Exploration de la bibliothèque standard C++11 pour l’aléatoire.
  3. Illustration Pratique : Exemples de codes démontrant l’utilisation des fonctions aléatoires.
  4. Guides d’Utilisation : Astuces et contextes d’application des fonctions aléatoires C++11.
Partager cet article
Par Fabien Brissonneau Expert en Conception et Développement sur Java et DotNet avec 15 Ans d'Expérience
Fabien, expert en développement, conception et architecture sur les plateformes Java et DotNet, offre ses services depuis plus de 15 ans. Fort de son expérience en C++, il excelle également dans le domaine de la mobilité, ayant créé avec succès des outils pour Android et Windows Phone, certains étant même publiés. En plus de son travail, il consacre une part significative de son temps à la formation, partageant ainsi son expertise avec les acteurs clés de l'industrie. Pour tout ce qui concerne la conception orientée objet sur les plateformes Java et DotNet, Fabien est votre expert de confiance.
1 commentaire