Le code ci-dessous est destiné à générer une liste de cinq nombres pseudo-aléatoires dans l'intervalle [1 100]. Je lance le default_random_engine
avec time(0)
, qui renvoie l'heure système en temps Unix . Lorsque je compile et exécute ce programme sur Windows 7 à l'aide de Microsoft Visual Studio 2013, il fonctionne comme prévu (voir ci-dessous). Quand je le fais dans Arch Linux avec le compilateur g ++, cependant, il se comporte étrangement.
Sous Linux, 5 numéros seront générés à chaque fois. Les 4 derniers numéros seront différents à chaque exécution (comme ce sera souvent le cas), mais le premier numéro restera le même.
Exemple de sortie de 5 exécutions sous Windows et Linux:
| Windows: | Linux:
---------------------------------------
Run 1 | 54,01,91,73,68 | 25,38,40,42,21
Run 2 | 46,24,16,93,82 | 25,78,66,80,81
Run 3 | 86,36,33,63,05 | 25,17,93,17,40
Run 4 | 75,79,66,23,84 | 25,70,95,01,54
Run 5 | 64,36,32,44,85 | 25,09,22,38,13
Ajoutant au mystère, ce premier nombre s'incrémente périodiquement de un sous Linux. Après avoir obtenu les sorties ci-dessus, j'ai attendu environ 30 minutes et j'ai essayé à nouveau de trouver que le 1er numéro avait changé et qu'il était maintenant toujours généré en 26. Il a continué à augmenter de 1 périodiquement et est maintenant à 32. Il semble correspondre avec la valeur changeante de time(0)
.
Pourquoi le premier nombre change-t-il rarement d'une exécution à l'autre et, quand c'est le cas, augmente-t-il de 1?
Le code. Il imprime proprement les 5 chiffres et l'heure du système:
#include <iostream>
#include <random>
#include <time.h>
using namespace std;
int main()
{
const int upper_bound = 100;
const int lower_bound = 1;
time_t system_time = time(0);
default_random_engine e(system_time);
uniform_int_distribution<int> u(lower_bound, upper_bound);
cout << '#' << '\t' << "system time" << endl
<< "-------------------" << endl;
for (int counter = 1; counter <= 5; counter++)
{
int secret = u(e);
cout << secret << '\t' << system_time << endl;
}
system("pause");
return 0;
}
sizeof(time_t)
vs.sizeof(default_random_engine::result_type)
?default_random_engine
c'est complètement différent sur ces deux plates-formes.Réponses:
Voici ce qui se passe:
default_random_engine
dans libstdc ++ (la bibliothèque standard de GCC) estminstd_rand0
, qui est un simple moteur congruentiel linéaire:La façon dont ce moteur génère des nombres aléatoires est x i + 1 = (16807x i + 0) mod 2147483647.
Par conséquent, si les graines sont différentes de 1, la plupart du temps, le premier nombre généré différera de 16807.
La portée de ce générateur est [1, 2147483646]. La façon dont libstdc ++ le
uniform_int_distribution
mappe à un entier dans la plage [1, 100] est essentiellement la suivante: générer un nombren
. Si le nombre n'est pas supérieur à 2147483600, retournez(n - 1) / 21474836 + 1
; sinon, réessayez avec un nouveau numéro.Il devrait être facile de voir que dans la grande majorité des cas, deux
n
s qui ne diffèrent que de 16807 donneront le même nombre en [1, 100] selon cette procédure. En fait, on s'attendrait à ce que le nombre généré augmente de un environ toutes les 21474836/16807 = 1278 secondes ou 21,3 minutes, ce qui correspond assez bien à vos observations.MSVC
default_random_engine
estmt19937
, qui n'a pas ce problème.la source
rand()
% 7 retourne toujours 0rand()
est quelque peu compréhensible (c'est de la merde héritée sans espoir). Utiliser un PRNG de niveau merde pour quelque chose de nouveau est inexcusable. Je considérerais même cela comme une violation de la norme, car la norme exige «de fournir au moins un comportement de moteur acceptable pour une utilisation relativement occasionnelle, inexpérimentée et / ou légère». que cette implémentation ne fournit pas car elle échoue de manière catastrophique, même pour des cas d'utilisation triviaux comme votrerand % 7
exemple.rand()
assez compréhensible exactement? Est-ce uniquement parce que personne n'aurait pensé à le faire?srand
est trop petite pour générer facilement des graines uniques. 3) Il renvoie un entier avec une limite supérieure définie par l'implémentation que l'appelant doit en quelque sorte réduire à un nombre dans la plage souhaitée, ce qui, lorsqu'il est fait correctement, est plus de travail que d'écrire un remplacement avec une API saine pourrand()
4) Il utilise un état mutable globalL'
std::default_random_engine
implémentation est définie. Utilisezstd::mt19937
ou à lastd::mt19937_64
place.De plus
std::time
et lesctime
fonctions ne sont pas très précises, utilisez<chrono>
plutôt les types définis dans l'en- tête:la source
std::random_device
au lieu de current_time pour semer votre générateur aléatoire. Veuillez vérifier tout exemple de référence sur Random.ctime
est de 1 seconde. La granularité desstd::chrono
implémentations est définie par l'utilisateur, par défaut, pourstd::high_resolution_clock
(dans Visual Studio, c'est un typedef pourstd::steady_clock
), nanosecondes, mais peut choisir une mesure beaucoup plus petite, donc beaucoup plus précise.Sous Linux, la fonction aléatoire n'est pas une fonction aléatoire au sens probabiliste du terme, mais un générateur de nombres pseudo aléatoires. Il est salé avec une graine, et sur la base de cette graine, les nombres qui sont produits sont pseudo aléatoires et uniformément distribués. La méthode Linux a l'avantage que dans la conception de certaines expériences utilisant des informations provenant de populations, la répétition de l'expérience avec des ajustements connus des informations d'entrée peut être mesurée. Lorsque le programme final est prêt pour les tests en conditions réelles, le sel (graine) peut être créé en demandant à l'utilisateur de déplacer la souris, de mélanger le mouvement de la souris avec quelques frappes et d'ajouter un tiret de microsecondes depuis le début de la dernière mise sous tension.
La graine de nombre aléatoire de Windows est obtenue à partir de la collection de numéros de souris, de clavier, de réseau et d'heure du jour. Ce n'est pas reproductible. Mais cette valeur de sel peut être réinitialisée à une graine connue, si comme mentionné ci-dessus, on est impliqué dans la conception d'une expérience.
Oh oui, Linux a deux générateurs de nombres aléatoires. Un, la valeur par défaut est modulo 32bits, et l'autre est modulo 64bits. Votre choix dépend des besoins de précision et de la quantité de temps de calcul que vous souhaitez utiliser pour vos tests ou votre utilisation réelle.
la source
collection of mouse, keyboard, network and time of day numbers