{"id":139,"date":"2016-12-09T10:38:36","date_gmt":"2016-12-09T10:38:36","guid":{"rendered":"http:\/\/renaudguezennec.eu\/?p=139"},"modified":"2025-08-17T20:12:12","modified_gmt":"2025-08-17T20:12:12","slug":"rand-moi-un-entier","status":"publish","type":"post","link":"http:\/\/renaudguezennec.eu\/index.php\/2016\/12\/09\/rand-moi-un-entier\/","title":{"rendered":"rand() moi un entier !"},"content":{"rendered":"<blockquote><p>Cet article est une synth\u00e8se de plusieurs conf\u00e9rences, de la documentation C++ et de mon exp\u00e9rience sur la g\u00e9n\u00e9ration de nombre al\u00e9atoire en C++.<\/p><\/blockquote>\n<h2>Mon besoin<\/h2>\n<p>Je suis le d\u00e9veloppeur de Rolisteam. Un logiciel pour faire du jeu de r\u00f4le en ligne. Il int\u00e8gre une solution badass (oui, je m&#8217;envoie des fleurs) de lancer de d\u00e9s. J&#8217;ai pass\u00e9 beaucoup de temps entre juillet 2014 et f\u00e9vrier 2015 pour produire un langage de script interpr\u00e9t\u00e9 de lancement de commandes de d\u00e9s. Vous pouvez y jeter un \u0153il sur ce lien: <a href=\"https:\/\/github.com\/Rolisteam\/DiceParser\" target=\"_blank\" rel=\"noopener\">DiceParser<\/a>. Bref, j&#8217;ai cr\u00e9e tout un syst\u00e8me pour lancer les d\u00e9s et faire des op\u00e9rations sur les r\u00e9sultats: tri, relance, filtre, explosion, arithm\u00e9tique, gestion des priorit\u00e9s math\u00e9matiques et il est m\u00eame capable de g\u00e9n\u00e9rer l&#8217;arbre d&#8217;ex\u00e9cution avec dot.<\/p>\n<h3>L\u2019al\u00e9atoire<\/h3>\n<p>Pour la partie al\u00e9atoire dans tout le syst\u00e8me, j&#8217;ai r\u00e9utilis\u00e9 le code historique de rolistik (l&#8217;anc\u00eatre de rolisteam).<br \/>\nCertains utilisateurs sont venus me voir pour me demander de v\u00e9rifier le syst\u00e8me d\u2019al\u00e9atoire car ils avaient fait des tests et avez d\u00e9termin\u00e9 que Rolisteam favorisait les r\u00e9sultats haut; ou bas selon les personnes.<br \/>\nJ&#8217;avais droit \u00e0 un tableau de statistique r\u00e9alis\u00e9 sur quelques dizaines de lancer de d\u00e9s.<br \/>\nArgument qu&#8217;il est facile de d\u00e9truire avec une \u00e9tude statistique plus riche en lancer. <a href=\"https:\/\/github.com\/Rolisteam\/DiceParser\" target=\"_blank\" rel=\"noopener\">DiceParser<\/a> fonctionne en ligne de commande, donc tr\u00e8s facile \u00e0 \u00abscripter\u00bb. Bref, des gens avaient des doutes sans r\u00e9elle preuve.<br \/>\nDu coup, je me suis un peu renseign\u00e9 s&#8217;il n&#8217;y avait pas des m\u00e9thodes plus \u00abC++\u00bb avec le C++11, C++14 et\/ou C++17.<\/p>\n<h2>L\u2019existant<\/h2>\n<p>Quand il s&#8217;agit de g\u00e9n\u00e9rer des nombres al\u00e9atoires, la premi\u00e8re impl\u00e9mentation qu&#8217;on apprend est souvent celle ci :<\/p>\n<p>[pastacode lang=&#8221;cpp&#8221; manual=&#8221;int%20resultat%20%3D%20std::rand()%25MAX%2Bdebut%3B&#8221; message=&#8221;usage classique de rand();&#8221; highlight=&#8221;&#8221; provider=&#8221;manual&#8221;\/]<\/p>\n<p>Je pense que tous les \u00e9tudiants ont impl\u00e9ment\u00e9 cette m\u00e9thode, dans leurs premi\u00e8res ann\u00e9es d&#8217;\u00e9tudes.\u00a0 Elle est facile \u00e0 comprendre. Elle est disponible dans beaucoup de langages. Elle vient du C. Elle met en avant le modulo, ce qui lui donne peut-\u00eatre une valeur p\u00e9dagogique. Elle suffit dans de tr\u00e8s nombreux cas.<\/p>\n<p>Nous allons voir un peu ce qui cloche avec cette m\u00e9thode.<\/p>\n<h3>Les d\u00e9fauts<\/h3>\n<p><strong>1\/ Pauvre entropie<\/strong><br \/>\nD\u00e9j\u00e0, <strong><span class=\"t-c\"><span class=\"mw-geshi cpp source-cpp\"><span class=\"nu0\">rand()<\/span><\/span><\/span><\/strong> ou <strong>std::rand()<\/strong> sont d\u00e9finis dans le &#8220;man&#8221; comme une solution pauvre pour obtenir de l\u2019al\u00e9atoire. Il est pr\u00e9cis\u00e9 que les vieilles impl\u00e9mentations de rand() ou les impl\u00e9mentations sur d&#8217;autres syst\u00e8mes fournissent un al\u00e9atoire pauvre sur les bits de poids faibles. Cela explique peut-\u00eatre pourquoi certains utilisateurs sous Windows se plaignent.<br \/>\nDonc l\u2019entropie de rand n&#8217;est pas bonne. Il existe une autre m\u00e9thode: <strong>random()<\/strong> qui fonctionne normalement mieux, avec un p\u00e9riode beaucoup plus grande.<\/p>\n<p><strong>2\/ Erreur de r\u00e9partition<\/strong><br \/>\nCes deux m\u00e9thodes g\u00e9n\u00e8rent un nombre entre<strong> 0<\/strong> et <strong>RAND_MAX<\/strong>. Maintenant, nous souhaitons obtenir des r\u00e9sultats sur un d\u00e9s \u00e0 100 faces. (C&#8217;est un format de d\u00e9s courant en Jeux de r\u00f4le).<br \/>\nImaginons que <strong>RAND_MAX<\/strong> vaut <span class=\"t-c\"><span class=\"mw-geshi cpp source-cpp\"><span class=\"nu0\">32767.<\/span><\/span><\/span><\/p>\n<p>Si je d\u00e9coupe la valeur maximum en tranche de 100 cela donne ceci:<\/p>\n<blockquote>\n<p style=\"text-align: center;\">[0 : 99] =&gt; 100 valeurs<br \/>\n<span class=\"t-c\"><span class=\"mw-geshi cpp source-cpp\"><span class=\"nu0\">[100 : 199] =&gt;<\/span><\/span><\/span> 100 valeurs<br \/>\n[200 : 299] =&gt; 100 valeurs<br \/>\n:<br \/>\n:<br \/>\n[32700 : 32767 ] =&gt; 67 valeurs.<\/p>\n<\/blockquote>\n<p>Nous avons 327 tranches compl\u00e8tes et une derni\u00e8re de 67.\u00a0 L&#8217;usage du modulo vient faire la correspondance entre la valeur tir\u00e9e dans l&#8217;espace de valeurs d\u00e9sir\u00e9es. Cela favorise les r\u00e9sultats entre 0 et 67. Il y a une chance suppl\u00e9mentaire de tomber en dessous de 67. La diff\u00e9rence est probablement n\u00e9gligeables mais tout de m\u00eame, cela me pose probl\u00e8me d&#8217;avoir cela dans mon logiciel.<\/p>\n<p><strong>3\/ Probl\u00e8me de portabilit\u00e9<\/strong><\/p>\n<p>La valeur <strong>RAND_MAX<\/strong> vaut au minimum 32767 dans la norme. Elle diff\u00e8rent en fonction des impl\u00e9mentations, nous avons vu \u00e9galement que la portabilit\u00e9 de la m\u00e9thode rand() n&#8217;est pas garantie.<br \/>\nCette somme de probl\u00e8mes vient conforter les utilisateurs de Rolisteam dans leur id\u00e9e de probl\u00e8me relatif au syst\u00e8me de d\u00e9s. Deux probl\u00e8mes de portabilit\u00e9 se cumulent.<\/p>\n<p>Le constat de ces erreurs m&#8217;a pouss\u00e9 \u00e0 chercher une solution moderne pour g\u00e9n\u00e9rer ces nombres.<\/p>\n<p>Dans les API C++ est arriv\u00e9 un nouveau &#8220;module&#8221; avec le C++11. Il s&#8217;agit de random.<\/p>\n<h2>#include &lt;random&gt;<\/h2>\n<p>Ce module pr\u00e9sente toute une s\u00e9rie de classes pour g\u00e9n\u00e9rer des nombres al\u00e9atoires et les r\u00e9partir.<br \/>\nIl propose un nombre int\u00e9ressant d&#8217;algorithmes ou de moteurs diff\u00e9rents pour la g\u00e9n\u00e9ration. Je ne suis pas un expert en g\u00e9n\u00e9ration pseudo-al\u00e9atoire de nombre. Du coup, le nom des algorithmes ne me parle pas.<br \/>\nCependant, il semble que deux \u00e9l\u00e9ments sortent du lot: le <strong>std::random_device<\/strong> et le <strong>std::mt19937<\/strong>.<\/p>\n<h3><strong>Le std::random_device<\/strong><\/h3>\n<p>Il est d\u00e9fini dans la documentation comme: Un g\u00e9n\u00e9rateur de nombre al\u00e9atoire non d\u00e9terministe. En gros, cela signifie qu&#8217;on ne peut pas pr\u00e9voir son comportement.<br \/>\nD&#8217;habitude, vous initialisez votre m\u00e9thode al\u00e9atoire par un seed. Il peut \u00eatre int\u00e9ressant pour des tests automatiques ou une r\u00e9solution de bug de pouvoir rejouer une s\u00e9quence enti\u00e8re de nombre al\u00e9atoire.<br \/>\nPour atteindre cet objectif, il suffit de mettre la m\u00eame seed. Donc vous avez l\u00e0 une m\u00e9thode de nombre al\u00e9atoire qui est d\u00e9terministe. random_device n&#8217;a pas de seed. Il n&#8217;a pas besoin d&#8217;\u00eatre initialiser. Il est donc impossible de lui faire &#8220;jouer&#8221; deux fois la m\u00eame chose. Dans mon cas, cela me satisfait compl\u00e8tement.<\/p>\n<p>C&#8217;est facile \u00e0 mettre en place:<\/p>\n<p>[pastacode lang=&#8221;cpp&#8221; manual=&#8221;std%3A%3Arandom_device%20rd%3B%0Astd%3A%3Auniform_int_distribution%3Cint%3E%20dist(1%2C%2010)%3B%0Aint%20randomNumber%20%3D%20dist(rd)%3B%0Astd%3A%3Acout%20%3C%3C%20%20randomNumber%20%3C%3C%20std%3A%3Aendl%3B&#8221; message=&#8221;&#8221; highlight=&#8221;&#8221; provider=&#8221;manual&#8221;\/]<\/p>\n<p>On g\u00e9n\u00e8re une nombre et on le distribue entre 1 et 10. Cela suffit. Il y a rien besoin de plus. C&#8217;est propre, \u00e9l\u00e9gant etc.<br \/>\nJ&#8217;ai impl\u00e9ment\u00e9 cette m\u00e9thode dans rolisteam 1.8, malgr\u00e9 les avertissements qu&#8217;on trouve \u00e0 droit \u00e0 gauche. Cela donne de bons r\u00e9sultats sous Linux. J&#8217;ai fait une version pour Windows et l\u00e0, c&#8217;est le drame.<br \/>\nL&#8217;entropie du module sous windows est de z\u00e9ro. Cela signifie qu&#8217;il renvoie toujours le m\u00eame r\u00e9sultat. Bref, les d\u00e9s \u00e9taient toujours de la m\u00eame valeur.<br \/>\nLa solution du random_device ne marche pas. Elle n&#8217;est pas portable.<\/p>\n<h3>Le <strong>std::mt19937<\/strong><\/h3>\n<p>Il reste donc la m\u00e9thode du <strong>std::mt19937<\/strong>. Elle fait toujours partie de l&#8217;API de &lt;random&gt;. Voici l&#8217;impl\u00e9mentation finale dans rolisteam.<\/p>\n<p>[pastacode lang=&#8221;cpp&#8221; manual=&#8221;%2F%2F%20dans%20le%20constructeur%20de%20ma%20classe%20d%C3%A9%0Aauto%20seed%20%3D%20std%3A%3Achrono%3A%3Ahigh_resolution_clock%3A%3Anow().time_since_epoch().count()%3B%0Am_rng%20%3D%20std%3A%3Amt19937(quintptr(this)%2Bseed)%3B%0A%0A%2F%2Fdans%20la%20fonction%20lancer%20(roll())%0Astd%3A%3Auniform_int_distribution%3Cqint64%3E%20dist(m_base%2Cm_faces)%3B%0Aqint64%20value%20%3D%20dist(m_rng)%3B&#8221; message=&#8221;L\u2019implementation dans rolisteam.&#8221; highlight=&#8221;&#8221; provider=&#8221;manual&#8221;\/]<\/p>\n<p>On peut voir qu&#8217;elle est plus proche de l&#8217;ancienne m\u00e9thode car Il est n\u00e9cessaire de l\u2019initialiser avec un seed. Pour cela, il est \u00e9galement possible d&#8217;utiliser une API C++11: &lt;chrono&gt;.<\/p>\n<p>[pastacode lang=&#8221;cpp&#8221; manual=&#8221;%20%23include%20%3Cchrono%3E%0A%E2%80%A6%0Aauto%20seed%20%3D%20std%3A%3Achrono%3A%3Ahigh_resolution_clock%3A%3Anow().time_since_epoch().count()%3B%0Am_rng%20%3D%20std%3A%3Amt19937(quintptr(this)%2Bseed)%3B&#8221; message=&#8221;Politique Agricole Commune&#8221; highlight=&#8221;&#8221; provider=&#8221;manual&#8221;\/]<\/p>\n<p>&nbsp;<\/p>\n<h2>Autres m\u00e9thodes:<\/h2>\n<p class=\"byline author vcard\">Il existe d&#8217;autres moyens de tirer des nombres al\u00e9atoires en C++. Il existe des tonnes de biblioth\u00e8ques C++ pour faire cela. Si j\u2019en avais une seule \u00e0 vous conseiller, je parlerais de PCG (<a href=\"http:\/\/www.pcg-random.org\">www.pcg-random.org<\/a>). Elle est \u00e9crite par Melissa <span class=\"byline-name fn\">O&#8217;Neill. Cheinan Marks fait l\u2019\u00e9loge de cette biblioth\u00e8que et du site qui l&#8217;accompagne. <\/span><\/p>\n<p class=\"byline author vcard\"><span class=\"byline-name fn\">Il y a de nombreux articles sur le sujet. Personnellement, je n&#8217;ai pas int\u00e9gr\u00e9 PCG dans rolisteam, tout simplement parce que chaque biblioth\u00e8ques externes est un calvaire \u00e0 g\u00e9rer dans le processus d&#8217;installation pour une application multi-plateforme.<br \/>\n<\/span><\/p>\n<h2>Conclusion:<\/h2>\n<p>J\u2019esp\u00e8re avoir fait le tour des nouvelles API pour la g\u00e9n\u00e9ration al\u00e9atoire de nombres entiers en C++. Il est important de garder \u00e0 l&#8217;esprit que l&#8217;API &lt;random&gt; fournit bien sur bien plus de chose que ce que j\u2019ai montr\u00e9. Je n&#8217;ai pas fait d\u2019\u00e9tude statistique pour d\u00e9montrer que c\u2019est mieux maintenant. Dans tous les cas, j\u2019ai impl\u00e9ment\u00e9 le device_random dans mon lecteur audio maison, et je d\u00e9couvre plein de nouvelle musique de ma collection.<\/p>\n<h2>Les sources:<\/h2>\n<p>CppCon 2016: Cheinan Marks \u201cI Just Wanted a Random Integer!&#8221;:<br \/>\n<a href=\"https:\/\/www.youtube.com\/watch?v=4_QO1nm7uJs\">https:\/\/www.youtube.com\/watch?v=4_QO1nm7uJs<\/a><br \/>\nStephan T. Lavavej &#8211; rand() Considered Harmful:<br \/>\n<a href=\"https:\/\/channel9.msdn.com\/Events\/GoingNative\/2013\/rand-Considered-Harmful\">https:\/\/channel9.msdn.com\/Events\/GoingNative\/2013\/rand-Considered-Harmful<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Cet article est une synth\u00e8se de plusieurs conf\u00e9rences, de la documentation C++ et de mon exp\u00e9rience sur la g\u00e9n\u00e9ration de nombre al\u00e9atoire en C++. Mon besoin Je suis le d\u00e9veloppeur de Rolisteam. Un logiciel pour faire du jeu de r\u00f4le en ligne. Il int\u00e8gre une solution badass (oui, je m&#8217;envoie des fleurs) de lancer de [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":162,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_import_markdown_pro_load_document_selector":0,"_import_markdown_pro_submit_text_textarea":"","footnotes":""},"categories":[80,23],"tags":[43,10,42,41],"class_list":["post-139","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-fr","category-tutorial","tag-c11","tag-qt","tag-rand","tag-random"],"_links":{"self":[{"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/posts\/139","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/comments?post=139"}],"version-history":[{"count":13,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/posts\/139\/revisions"}],"predecessor-version":[{"id":5075,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/posts\/139\/revisions\/5075"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/media\/162"}],"wp:attachment":[{"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/media?parent=139"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/categories?post=139"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/tags?post=139"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}