C++ 11 a introduit l'en-tête <random>
avec des déclarations pour les moteurs de nombres aléatoires et les distributions aléatoires. C’est formidable - il est temps de remplacer les utilisations de Rand()
, qui sont souvent problématiques 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 distribution uniforme puisse être construite à peu près comme ceci:
std::default_random_engine engine;
engine.seed(n);
std::uniform_int_distribution<> 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 <random>
représente un énorme pas en avant à cet égard. Quelques lignes de code et moi avons un générateur très agréable qui se comporte très bien et qui génère facilement des variables aléatoires à partir de nombreuses distributions différentes.
Compte tenu de ce qui précède, ma réponse à vous est un peu hérétique. Si Rand()
suffit à vos besoins, utilisez-le. Aussi mauvais que soit Rand()
(et c'est mauvais), le supprimer représenterait une rupture 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 la disparition de Rand()
. Il y a de fortes chances que vous vous retiriez ou changiez de langue avant que 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 comme
std::vector<int> v(size); std::generate(v.begin(), v.end(), std::Rand);
Vous n'avez pas besoin d'un PRNG sécurisé sur le plan cryptographique 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 remplacement simple de Nice pour Rand()
et srand()
dans la bibliothèque aléatoire C++ 11: std::minstd_Rand
.
#include <random>
#include <iostream>
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 231-2, inclus. Cela signifie que le résultat sera toujours converti en un std::int_fast32_t
(ou en une int
si int
a au moins 32 bits).
Que diriez-vous de randutils
de Melissa O'Neill de pcg-random.org ?
Extrait du 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
de style C, y compris leur bizarrerie, mais avec une bonne aléatoire, c’est le plus proche que je pourrais obtenir.
#include <random>
#include <cstdlib> // Rand_MAX (might be removed soon?)
#include <climits> // 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_distribution<int>&
get_distribution() noexcept
{
static thread_local auto rnddst = std::uniform_int_distribution<int> {0, Rand_max};
return rnddst;
}
} // namespace detail
inline int
Rand() noexcept
{
return detail::get_distribution()(detail::get_engine());
}
inline void
srand(const unsigned seed) noexcept
{
detail::get_engine().seed(seed);
detail::get_distribution().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 <cstdlib>
. 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 distribution sont conservés sous la forme d'instances thread_local
static
, de sorte qu'elles 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 sommes 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, ont également été observés comme étant plus rapides . Je ne pense pas qu'un programme conforme doive compter sur la mise en œuvre de std::Rand
à l'aide d'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.)
Tous les moteurs définis dans <random>
ont une operator()()
qui peut être utilisée pour récupérer 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 de type non signé intégral, ce qui signifie qu'ils peuvent potentiellement déborder d'une intégrale signée (qui conduira alors à comportement indéfini).
Si vous pouvez utiliser les valeurs unsigned _ _ où que vous récupériez 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, en raison du fait que le
result_type
duengine
n'ayant pas une valeur maximale égale à un multiple pair de la valeur la plus élevée pouvant être stockée dans le type de destination.
Si vous ne vous en êtes pas inquiété par le passé - lorsque vous utilisez quelque chose commeRand()%low+high
- vous ne devriez pas vous en préoccuper pour le moment.
Remarque : Vous devrez vous assurer que le
std::engine-type::result_type
est au moins aussi grand que la plage de valeurs souhaitée (std::mt19937::result_type
estuint_fast32_t
).
Il n’est pas nécessaire d’avoir d’abord default-construct un std::default_random_engine
(qui est juste un typedef pour un moteur choisi par le implémentation), puis de lui affecter il; cela pourrait être fait en une fois en utilisant le constructeur approprié du random-engine.
std::random-engine-type engine (seed);
Si vous avez cependant besoin de réamorcer le moteur, utilisez std::random-engine::seed
Vous pouvez créer une fonction simple comme ceci:
#include <random>
#include <iostream>
int modernRand(int n) {
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, n);
return dis(gen);
}
Et plus tard, utilisez-le comme ceci:
int myRandValue = modernRand(n);
Comme mentionné ici