<random> генерирует одинаковое число в Linux, но не в Windows

Приведенный ниже код предназначен для создания списка из пяти псевдослучайных чисел в интервале [1,100]. Я семя default_random_engine с time(0), который возвращает системное время в unix time. Когда я компилирую и запускаю эту программу в Windows 7 с помощью Microsoft Visual Studio 2013, она работает как ожидается (см. Ниже). Однако, когда я делаю это в Arch Linux с компилятором g++, это ведет себя странно.

В Linux будет генерироваться 5 чисел каждый раз. Последние 4 числа будут отличаться при каждом выполнении (как это часто бывает), но первое число останется неизменным.

Пример вывода из 5 исполнений в Windows и 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

Добавляя к тайне, это первое число периодически увеличивается на единицу в Linux. Получив вышеуказанные выходы, я подождал около 30 минут и снова попытался найти, что 1-й номер изменился, и теперь он всегда генерируется как 26. Он продолжает увеличиваться на 1 периодически и теперь равен 32. Кажется, что это соответствует с изменяющимся значением time(0).

Почему первое число редко изменяется в разных записях, а затем, когда оно происходит, увеличивается на 1?

Код. Он аккуратно выводит 5 номеров и системное время:

#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;
}

Ответ 1

Вот что происходит:

  • default_random_engine в libstdС++ (стандартная библиотека GCC) есть minstd_rand0, который является простым линейным конгруэнтным движком:

    typedef linear_congruential_engine<uint_fast32_t, 16807, 0, 2147483647> minstd_rand0;
    
  • Способ, которым этот двигатель генерирует случайные числа, - это x я + 1= (16807x i + 0) mod 2147483647.

  • Следовательно, если семена отличаются на 1, то большую часть времени первое сгенерированное число будет отличаться на 16807.

  • Диапазон этого генератора равен [1, 2147483646]. Способ, которым libstdС++ uniform_int_distribution сопоставляет его с целым числом в диапазоне [1, 100], по существу таков: сгенерируйте число n. Если число не превышает 2147483600, верните (n - 1) / 21474836 + 1; в противном случае повторите попытку с новым номером.

    Легко видеть, что в подавляющем большинстве случаев два n, которые отличаются только 16807, будут давать то же число в [1, 100] в соответствии с этой процедурой. Фактически, можно было бы ожидать, что сгенерированное число увеличится на один примерно каждые 21474836/16807 = 1278 секунд или 21,3 минуты, что очень хорошо согласуется с вашими наблюдениями.

MSVC default_random_engine - mt19937, который не имеет этой проблемы.

Ответ 2

std::default_random_engine определена реализация. Вместо этого используйте std::mt19937 или std::mt19937_64.

Кроме того, функции std::time и ctime не очень точны, используйте типы, определенные в заголовке <chrono>:

#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;
}

Ответ 3

В Linux случайная функция не является случайной функцией в вероятностном смысле пути, а генератором псевдослучайных чисел. Соленая с семенами, и на основе этого семени, числа, которые производятся, являются псевдослучайными и равномерно распределенными. Преимущество Linux заключается в том, что при разработке определенных экспериментов с использованием информации из популяций можно измерить повторение эксперимента с известной настройкой входной информации. Когда окончательная программа готова к реальному тестированию, соль (семя) может быть создана, попросив пользователя переместить мышь, смешать движение мыши с помощью некоторых нажатий клавиш и добавить тире микросекундных отсчетов с начала последняя включенность.

Серийное число случайных чисел Windows получается из коллекции номеров мыши, клавиатуры, сети и времени суток. Это не повторяемо. Но это значение соли может быть reset для известного семени, если, как упоминалось выше, один участвует в разработке эксперимента.

О да, у Linux есть два генератора случайных чисел. Один, по умолчанию - по модулю 32 бита, а другой - по модулю 64 бита. Ваш выбор зависит от потребностей в точности и количества времени вычисления, которое вы хотите использовать для тестирования или фактического использования.