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 <
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 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 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 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 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 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
En Résumé :
- Nouveautés en C++11 : Introduction aux avancées de C++11, en particulier pour la génération aléatoire.
- Bibliothèque de Génération Aléatoire : Exploration de la bibliothèque standard C++11 pour l’aléatoire.
- Illustration Pratique : Exemples de codes démontrant l’utilisation des fonctions aléatoires.
- Guides d’Utilisation : Astuces et contextes d’application des fonctions aléatoires C++11.
Votre exposé est très bien fait et très clair ce qui m’amène à vous demander conseil. Je viens de publier un livre sur le C++ aux éditions ENI (« Langage C++, L’héritage du C et la programmation orientée objet »). Il s’agit d’un livre qui s’adresse à des débutants et j’aimerais beaucoup avoir l’avis de personnes qui comme vous connaissent le C++ avec à l’évidence une compétence pédagogique. Jusqu’à maintenant ce livre est assez mal accueilli par la communauté C++. Il faut dire que je n’en fais pas partie, à l’origine je suis artiste numérique et pour moi le programmeur est d’abord un écrivain. Ce que j’aime c’est explorer un langage avec ses possibilités de sens. Mon livre n’est certes pas parfait. J’ai pensé que peut-être vous pourriez m’aider à pointer des axes d’améliorations. En vous remerciant,