web-dev-qa-db-fra.com

<aléatoire> génère le même nombre sous Linux, mais pas sous Windows

Le code ci-dessous est destiné à générer une liste de cinq nombres pseudo-aléatoires dans l'intervalle [1 100]. J'amorce le default_random_engine Avec time(0), qui renvoie l'heure système en heure unix . Lorsque je compile et exécute ce programme sur Windows 7 à l'aide de Microsoft Visual Studio 2013, cela fonctionne comme prévu (voir ci-dessous). Quand je le fais dans Arch Linux avec le compilateur g ++, cependant, cela 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 sur 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 incrémente périodiquement d'un sous Linux. Après avoir obtenu les sorties ci-dessus, j'ai attendu environ 30 minutes et j'ai essayé à nouveau de constater que le 1er nombre avait changé et était toujours généré en tant que 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 série à l'autre, puis quand il le fait, incrémente de 1?

Le code. Il imprime parfaitement 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;
}
88
Amin Mesbah

Voici ce qui se passe:

  • default_random_engine Dans libstdc ++ (bibliothèque standard de GCC) est minstd_Rand0, Qui est un moteur congruentiel linéaire simple:

    typedef linear_congruential_engine<uint_fast32_t, 16807, 0, 2147483647> minstd_Rand0;
    
  • La façon dont ce moteur génère des nombres aléatoires est xi + 1 = (16807xje + 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ère de 16807.

  • La plage de ce générateur est [1, 2147483646]. La façon dont uniform_int_distribution De libstdc ++ le mappe à un entier dans la plage [1, 100] est essentiellement la suivante: générer un nombre n. Si le nombre n'est pas supérieur à 2147483600, retournez (n - 1) / 21474836 + 1; sinon, essayez à nouveau avec un nouveau numéro.

    Il devrait être facile de voir que dans la grande majorité des cas, deux n qui ne diffèrent que de 16807 donneront le même nombre en [1, 100] dans le cadre de cette procédure. En fait, on s'attendrait à ce que le nombre généré augmente d'une unité toutes les 21474836/16807 = 1278 secondes ou 21,3 minutes, ce qui correspond assez bien à vos observations.

default_random_engine De MSVC est mt19937, Qui n'a pas ce problème.

139
T.C.

Le std::default_random_engine est défini par l'implémentation. Utilisation std::mt19937 ou std::mt19937_64 au lieu.

En plus std::time et les fonctions ctime ne sont pas très précises, utilisez les types définis dans le <chrono> en-tête à la place:

#include <iostream>
#include <random>
#include <chrono>

int main()
{
    const int upper_bound = 100;
    const int lower_bound = 1;

    auto t = std::chrono::high_resolution_clock::now().time_since_Epoch().count();

    std::mt19937 e;
    e.seed(static_cast<unsigned int>(t)); //Seed engine with timed value.
    std::uniform_int_distribution<int> u(lower_bound, upper_bound);

    std::cout << '#' << '\t' << "system time" << std::endl
    << "-------------------" << std::endl;

    for (int counter = 1; counter <= 5; counter++)
    {
        int secret = u(e);

        std::cout << secret << '\t' << t << std::endl;
    }   

    system("pause");
    return 0;
}
28
Casey