在 Linux 中生成相同的数字,但在 Windows 中不生成

下面的代码意味着在间隔[1,100]中生成一个由5个伪随机数组成的列表。我在 default_random_engine中引入了 time(0),它返回 Unix 时间中的系统时间。当我使用 MicrosoftVisualStudio2013在 Windows7上编译和运行这个程序时,它按预期工作(见下文)。然而,当我在 Arch Linux 中使用 g + + 编译器这样做时,它的行为很奇怪。

在 Linux 中,每次生成5个数字。最后4个数字在每次执行时都不同(通常都是这样) ,但是第一个数字保持不变。

Windows 和 Linux 上5次执行的输出示例:

      | 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 上,第一个数字周期性地递增1。获得上述输出后,我等待了大约30分钟,并再次尝试发现,第一个数字已经改变,现在总是生成为一个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;
}
8415 次浏览

The std::default_random_engine is implementation defined. Use std::mt19937 or std::mt19937_64 instead.

In addition std::time and the ctime functions are not very accurate, use the types defined in the <chrono> header instead:

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

Here's what's going on:

  • default_random_engine in libstdc++ (GCC's standard library) is minstd_rand0, which is a simple linear congruential engine:

    typedef linear_congruential_engine<uint_fast32_t, 16807, 0, 2147483647> minstd_rand0;
    
  • The way this engine generates random numbers is xi+1 = (16807xi + 0) mod 2147483647.

  • Therefore, if the seeds are different by 1, then most of the time the first generated number will differ by 16807.

  • The range of this generator is [1, 2147483646]. The way libstdc++'s uniform_int_distribution maps it to an integer in the range [1, 100] is essentially this: generate a number n. If the number is not greater than 2147483600, then return (n - 1) / 21474836 + 1; otherwise, try again with a new number.

    It should be easy to see that in the vast majority of cases, two ns that differ by only 16807 will yield the same number in [1, 100] under this procedure. In fact, one would expect the generated number to increase by one about every 21474836 / 16807 = 1278 seconds or 21.3 minutes, which agrees pretty well with your observations.

MSVC's default_random_engine is mt19937, which doesn't have this problem.

In Linux, the random function is not a random function in the probabilistic sense of the way, but a pseudo random number generator. It is salted with a seed, and based on that seed, the numbers that are produced are pseudo random and uniformly distributed. The Linux way has the advantage that in the design of certain experiments using information from populations, that the repeat of the experiment with known tweaking of input information can be measured. When the final program is ready for real-life testing, the salt (seed), can be created by asking for the user to move the mouse, mix the mouse movement with some keystrokes and add in a dash of microsecond counts since the beginning of the last power on.

Windows random number seed is obtained from the collection of mouse, keyboard, network and time of day numbers. It is not repeatable. But this salt value may be reset to a known seed, if as mentioned above, one is involved in the design of an experiment.

Oh yes, Linux has two random number generators. One, the default is modulo 32bits, and the other is modulo 64bits. Your choice depends on the accuracy needs and amount of compute time you wish to consume for your testing or actual use.