<random> generates the same number on Linux, but not on Windows
The code below is intended to create a list of five pseudorandom numbers in the interval [1,100]. I am the seed of default_random_engine with time(0) , which returns the system time at unix time . When I compile and run this program on Windows 7 using Microsoft Visual Studio 2013, it works as expected (see below). However, when I do this on Arch Linux with the g ++ compiler, this behaves strangely.
On Linux, 5 numbers will be generated each time. The last 4 numbers will differ at each execution (as is often the case), but the first number will remain unchanged.
Example output from 5 executions on Windows and 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 Adding to the secret, this first number periodically increases by one in Linux. Having received the above outputs, I waited about 30 minutes and again tried to find that the 1st number has changed, and now it is always generated as 26. It continues to increase by 1 periodically and now equals 32. It seems to correspond with a changing value of time(0) .
Why does the first number rarely change in different records, and then, when it occurs, increases by 1?
The code. It accurately displays 5 numbers and system time:
#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; } Here's what happens:
default_random_enginein libstdc ++ (the GCC standard library) isminstd_rand0, which is a simple linear congruent engine:typedef linear_congruential_engine<uint_fast32_t, 16807, 0, 2147483647> minstd_rand0;The way this engine generates random numbers is x i + 1 = (16807x i + 0) mod 2147483647.
Therefore, if the seeds differ 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 that libstdC ++
uniform_int_distributionmatches it with an integer in the range [1, 100] is essentially this: generate the numbern. If the number does not exceed 2147483600, return(n - 1) / 21474836 + 1; otherwise, try again with the new number.It is easy to see that in the vast majority of cases, two
n, which differ only 16807, will give the same number in [1, 100] in accordance with this procedure. In fact, one would expect the generated number to increase by about one every 21474836/16807 = 1278 seconds or 21.3 minutes, which is very well consistent with your observations.
MSVC default_random_engine is mt19937 that does not have this problem.
std::default_random_engine implementation defined. Use std::mt19937 or std::mt19937_64 .
Also, the std::time and ctime not very accurate, use the types defined in the <chrono> header:
#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; } In Linux, a random function is not a random function in the probabilistic sense of a path, but a pseudo-random number generator. Salted with seeds, and based on this seed, the numbers that are produced are pseudo-random and evenly distributed. The advantage of Linux is that when developing certain experiments using information from populations, you can measure the repetition of an experiment with a known input setting. When the final program is ready for real testing, salt (seed) can be created by asking the user to move the mouse, mix the mouse movement with some keystrokes and add a dash of microsecond readings from the last turn on.
The Windows random number serial is obtained from the collection of mouse, keyboard, network, and time of day numbers. This is not repeatable. But this salt value can be reset for a known seed if, as mentioned above, one is involved in the development of an experiment.
Oh yes, Linux has two random number generators. One, by default - modulo 32 bits, and the other - modulo 64 bits. Your choice depends on the accuracy needs and the amount of calculation time that you want to use for testing or actual use.