<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 бита. Ваш выбор зависит от потребностей в точности и количества времени вычисления, которое вы хотите использовать для тестирования или фактического использования.