web-dev-qa-db-fra.com

Pourquoi ne pas simplement utiliser random_device?

Je suis un peu confus au sujet de la bibliothèque aléatoire c ++ 11.

Ce que je comprends: nous avons besoin de deux concepts distincts:

  • moteur aléatoire (qui peut être pseudo (besoin de graines) ou réel)
  • distribution: il mappe les nombres obtenus du moteur à un intervalle spécifique, en utilisant une distribution spécifique.

Ce que je ne comprends pas, c'est pourquoi ne pas simplement utiliser ceci:

std::random_device rd;
std::uniform_int_distribution<int> dist(1, 5);

// get random numbers with:
dist(rd);

Pour autant que je sache, cela fonctionne bien.

Au lieu de cela, c'est ce que j'ai trouvé sur la plupart des exemples/sites/articles:

std::random_device rd;
std::mt19937 e{rd()}; // or std::default_random_engine e{rd()};
std::uniform_int_distribution<int> dist{1, 5};

// get random numbers with:
dist(e);

Je ne parle pas d'une utilisation spéciale, par ex. la cryptographie, juste vos articles de base pour commencer.

Mes soupçons sont parce que std::mt19937 (ou std::default_random_engine) accepte une graine, il peut être plus facile de déboguer en fournissant la même graine lors d'une session de débogage.

Et pourquoi pas seulement:

std::mt19937 e{std::random_device{}()};
24
bolov

Et pourquoi pas seulement:

std::mt19937 e{std::random_device{}()};

Cela pourrait être bien si vous ne le faites qu'une seule fois, mais si vous le faites plusieurs fois, il est préférable de garder une trace de votre std::random_device Et de ne pas le créer/le détruire inutilement.

Il peut être utile de regarder le code source de libc ++ pour l'implémentation de std::random_device, Qui est assez simple. C'est juste une mince enveloppe sur std::fopen("/dev/urandom"). Ainsi, chaque fois que vous créez un std::random_device, Vous obtenez un autre descripteur de système de fichiers et payez tous les coûts associés.

Sur Windows, si je comprends bien, std::random_device Représente un appel à une API de chiffrement Microsoft, donc vous allez initialiser et détruire une interface de bibliothèque de chiffrement à chaque fois que vous faites cela.

Cela dépend de votre application, mais à des fins générales, je ne pense pas que ces frais généraux soient toujours négligeables. Parfois c'est le cas, et c'est super.

Je suppose que cela rejoint votre première question:

Au lieu de cela, c'est ce que j'ai trouvé sur la plupart des exemples/sites/articles:

 std::random_device rd;
 std::mt19937 e{rd()}; // or std::default_random_engine e{rd()};
 std::uniform_int_distribution<int> dist{1, 5};

Au moins la façon dont j'y pense est:

  • std::mt19937 Est un générateur aléatoire très simple et fiable. Il est autonome et vivra entièrement dans votre processus, sans appeler le système d'exploitation ni rien d'autre. L'implémentation est mandatée par la norme, et au moins en boost, elle a utilisé partout le même code, dérivé du papier original mt19937. Ce code est très stable et il est multiplateforme. Vous pouvez être assez confiant que l'initialisation, l'interrogation, etc. va compiler vers un code similaire sur n'importe quelle plate-forme sur laquelle vous le compilez, et que vous obtiendrez des performances similaires.

  • std::random_device En revanche est assez opaque. Vous ne savez pas exactement ce que c'est, ce qu'il va faire, ni son efficacité. Vous ne savez même pas si elle peut être réellement acquise - cela pourrait lever une exception lorsque vous essayez de la créer. Vous savez que cela ne nécessite pas de graine. Vous n'êtes généralement pas censé en extraire des tonnes et des tonnes de données, utilisez-les simplement pour générer des graines. Parfois, il agit comme une interface agréable avec les API cryptographiques, mais ce n'est pas vraiment nécessaire pour le faire et malheureusement, parfois non. Cela peut correspondre à /dev/random Sur unix, cela peut correspondre à /dev/urandom/. Il peut correspondre à une API de chiffrement MSVC (Visual Studio) ou à une constante fixe (mingw). Si vous effectuez une compilation croisée pour un téléphone, qui sait ce qu'il fera. (Et même lorsque vous obtenez /dev/random, Vous avez toujours le problème que les performances peuvent ne pas être cohérentes - cela peut sembler fonctionner très bien, jusqu'à ce que le pool d'entropie soit épuisé, puis il court lentement comme un chien.)

La façon dont j'y pense est que std::random_device Est censé être comme une version améliorée de l'ensemencement avec time(NULL) - c'est une barre basse, parce que time(NULL) est une jolie graine de merde tout bien considéré. Je l'utilise généralement là où j'aurais utilisé time(NULL) pour générer une graine, dans la journée. Je ne pense pas vraiment que tout cela soit utile en dehors de cela.

26
Chris Beck

Cet article est un bon point de départ.

Je vais synthétiser quelques points:

  • Son coût est inconnu.

    Combien coûte la lecture d'un numéro sur cet "appareil"? C'est indéterminé. Il pourrait, par exemple, lire à partir de/dev/random sur un système Linux, qui peut bloquer pendant une longue période en attendant l'entropie (ce qui est lui-même problématique pour diverses raisons).

Pour mon expérience personnelle, j'ai signalé que std::random_device est généralement plus lent qu'un simple algorithme pseudo-aléatoire. Cela pourrait ne pas être vrai en général, mais c'est généralement le cas. En effet, cela peut impliquer des périphériques physiques ou un autre matériel que le simple processeur.

  • Cela pourrait en fait être déterministe.

    Std :: random_device de C++ 11 ne doit pas être non déterministe! Les implémentations peuvent et l'implémentent comme un RNG simple avec une graine fixe, donc il produit la même sortie pour chaque exécution du programme.

10
Biagio Festa