Comment utiliser pour remplacer rand ()?

C ++ 11 a introduit l’en-tête avec des déclarations pour les moteurs de nombres aléatoires et les dissortingbutions aléatoires. C’est formidable – il est temps de remplacer les utilisations de rand() qui pose souvent des problèmes de diverses manières. Cependant, il semble loin d’être évident de savoir comment remplacer

 srand(n); // ... int r = rand(); 

Sur la base des déclarations, il semble qu’une dissortingbution uniforme puisse être construite à peu près comme ceci:

 std::default_random_engine engine; engine.seed(n); std::uniform_int_dissortingbution distribution; auto rand = [&](){ return distribution(engine); } 

Cette approche semble plutôt impliquée et est sûrement quelque chose dont je ne me souviendrai pas, contrairement à l’utilisation de srand() et rand() . Je suis au courant de N4531 mais même cela semble toujours assez impliqué.

Existe-t-il un moyen assez simple de remplacer srand() et rand() ?

Existe-t-il un moyen assez simple de remplacer srand () et rand ()?

Divulgation complète: Je n’aime pas rand() . C’est mauvais, et c’est très facilement abusé.

La bibliothèque aléatoire C ++ 11 comble un vide qui fait défaut depuis très longtemps. Le problème avec les bibliothèques aléatoires de haute qualité est qu’elles sont souvent difficiles à utiliser. La bibliothèque C ++ 11 représente un énorme pas en avant à cet égard. Quelques lignes de code et j’ai un très bon générateur qui se comporte très bien et qui génère facilement des variables aléatoires à partir de nombreuses dissortingbutions différentes.

Compte tenu de ce qui précède, ma réponse est un peu hérétique. Si rand() est suffisant pour vos besoins, utilisez-le. Aussi mauvais que rand() est (et c’est mauvais), le supprimer représenterait une rupture énorme avec le langage C. Assurez-vous simplement que la méchanceté de rand() est vraiment suffisante pour vos besoins.

C ++ 14 n’a pas déprécié rand() ; seules les fonctions obsolètes de la bibliothèque C ++ utilisant rand() . Bien que C ++ 17 risque de rendre obsolète rand() , il ne le supprimera pas. Cela signifie que vous avez encore plusieurs années avant que rand() disparaisse. Il y a de fortes chances que vous vous retiriez ou que vous changiez de langue au moment où le comité C ++ supprime enfin rand() de la bibliothèque standard C ++.

Je crée des entrées aléatoires pour comparer différentes implémentations de std :: sort () en utilisant quelque chose le long des lignes de std::vector v(size); std::generate(v.begin(), v.end(), std::rand); std::vector v(size); std::generate(v.begin(), v.end(), std::rand);

Vous n’avez pas besoin d’un PRNG cryptographiquement sécurisé pour cela. Vous n’avez même pas besoin de Mersenne Twister. Dans ce cas particulier, rand() est probablement suffisant pour vos besoins.

Mettre à jour
Il existe un joli remplacement simple pour rand() et srand() dans la bibliothèque aléatoire C ++ 11: std::minstd_rand .

 #include  #include  int main () { std:: minstd_rand simple_rand; // Use simple_rand.seed() instead of srand(): simple_rand.seed(42); // Use simple_rand() instead of rand(): for (int ii = 0; ii < 10; ++ii) { std::cout << simple_rand() << '\n'; } } 

La fonction std::minstd_rand::operator()() renvoie un std::uint_fast32_t . Cependant, l’algorithme limite le résultat entre 1 et 2 31 -2 inclus. Cela signifie que le résultat sera toujours converti en toute sécurité en std::int_fast32_t (ou en int si 32 bits au moins).

Que diriez-vous de randutils par Melissa O’Neill de pcg-random.org ?

De l’ article de blog d’introduction :

 randutils::mt19937_rng rng; std::cout << "Greetings from Office #" << rng.uniform(1,17) << " (where we think PI = " << rng.uniform(3.1,3.2) << ")\n\n" << "Our office morale is " << rng.uniform('A','D') << " grade\n"; 

En supposant que vous souhaitiez le comportement des fonctions rand et srand style C, y compris leur bizarrerie, mais avec une bonne aléatoire, c’est le plus proche que je puisse obtenir.

 #include  #include  // RAND_MAX (might be removed soon?) #include  // INT_MAX (use as replacement?) namespace replacement { constexpr int rand_max { #ifdef RAND_MAX RAND_MAX #else INT_MAX #endif }; namespace detail { inline std::default_random_engine& get_engine() noexcept { // Seeding with 1 is silly, but required behavior static thread_local auto rndeng = std::default_random_engine(1); return rndeng; } inline std::uniform_int_dissortingbution& get_dissortingbution() noexcept { static thread_local auto rnddst = std::uniform_int_dissortingbution {0, rand_max}; return rnddst; } } // namespace detail inline int rand() noexcept { return detail::get_dissortingbution()(detail::get_engine()); } inline void srand(const unsigned seed) noexcept { detail::get_engine().seed(seed); detail::get_dissortingbution().reset(); } inline void srand() { std::random_device rnddev {}; srand(rnddev()); } } // namespace replacement 

Les fonctions replacement::* peuvent être utilisées exactement comme leurs homologues std::* de . J’ai ajouté une surcharge srand qui ne prend pas d’argument et qui ensemence le moteur avec un “vrai” nombre aléatoire obtenu à partir d’un std::random_device . La «réalité» de ce caractère aléatoire sera bien entendu définie par la mise en œuvre.

Le moteur et la dissortingbution sont conservés sous la thread_local static instances static thread_local , de sorte qu’ils portent l’état dans plusieurs appels tout en permettant à différents threads d’observer des séquences prévisibles. (Il s’agit également d’un gain de performance, car vous n’avez pas besoin de reconstruire le moteur ni d’utiliser des verrous et potentiellement de jeter les sums versées par d’autres personnes.)

J’ai utilisé std::default_random_engine parce que vous l’avez fait mais je ne l’aime pas beaucoup. Les moteurs Twister de Mersenne ( std::mt19937 et std::mt19937_64 ) produisent un bien meilleur «caractère aléatoire» et, étonnamment, ils ont également été observés comme étant plus rapides . Je ne pense pas qu’un programme conforme doive compter sur std::rand étant implémenté en utilisant un type spécifique de moteur pseudo aléatoire. (Et même si c’était le cas, les implémentations sont libres de définir std::default_random_engine comme bon leur semble, vous devrez donc utiliser quelque chose comme std::minstd_rand pour en être sûr.)

Abuser du fait que les moteurs renvoient directement des valeurs

Tous les moteurs définis dans ont un operator()() qui peut être utilisé pour extraire la valeur générée suivante, ainsi que pour faire avancer l’état interne du moteur.

 std::mt19937 rand (seed); // or an engine of your choosing 
 for (int i = 0; i < 10; ++i) { unsigned int x = rand (); std::cout << x << std::endl; } 

Il convient toutefois de noter que tous les moteurs renvoient une valeur d'un type entier non signé , ce qui signifie qu'ils peuvent potentiellement dépasser une intégrale signée (ce qui conduira ensuite à un comportement indéfini ).

Si vous pouvez utiliser des valeurs non signées partout où vous récupérez une nouvelle valeur, la procédure ci-dessus constitue un moyen simple de remplacer l'utilisation de std::srand + std::rand .

Remarque : L'utilisation de ce qui a été décrit ci-dessus peut augmenter le risque de renvoi de certaines valeurs par rapport à d'autres, du fait que le type de résultat du engine ne comporte pas une valeur max égale à la multiple de la valeur la plus élevée pouvant être stocké dans le type de destination.
Si vous ne vous en êtes pas inquiété par le passé (lorsque vous utilisez quelque chose comme rand()%low+high , ne vous en inquiétez pas maintenant.

Remarque : Vous devrez vous assurer que la valeur std:: engine-type ::result_type est au moins aussi grande que la plage de valeurs souhaitée ( std::mt19937::result_type est uint_fast32_t ).


Si vous devez seulement semer le moteur une fois

Il n'est pas nécessaire de commencer par défaut à construire std::default_random_engine (qui est simplement un typedef pour un moteur choisi par l' implémentation ), puis à lui assigner un germe; cela pourrait être fait d'un coup en utilisant le constructeur approprié du moteur aléatoire .

 std:: random-engine-type engine (seed); 

Si vous avez toutefois besoin de réamorcer le moteur, utilisez std:: random-engine ::seed .


Si tout échoue, créer une fonction d'assistance

Même si le code que vous avez posté semble un peu compliqué, vous n'êtes censé l'écrire qu'une seule fois.

Si vous vous trouvez dans une situation où vous êtes tenté de simplement copier / coller ce que vous avez écrit à plusieurs endroits de votre code, il est recommandé, comme toujours lorsque vous faites du copier-coller ; introduire une fonction d'assistance.

 Intentionally left blank, see other posts for example implementations. 

Vous pouvez créer une fonction simple comme ceci:

 #include  #include  int modernRand(int n) { std::random_device rd; std::mt19937 gen(rd()); std::uniform_int_dissortingbution<> dis(0, n); return dis(gen); } 

Et plus tard, utilisez-le comme ceci:

 int myRandValue = modernRand(n); 

Comme mentionné ici