C++11 <random> is not random?

Oct 11, 2016 at 5:50am
I'm having a heck of a time with C++11's <random>. First I tried seeding with random_device and that worked on Mac/Clang and Windows/Visual Studio, but it's broke on GCC (both Windows and Linux) because it returns the same deterministic series of numbers.

From there I've been playing with the Mersenne Twister mt19937_64 and seeding it with chrono::system_clock::now, it appears to be working perfectly fine on my Mac. However, when I tried it on Windows/GCC/DevC++ it spits out the same number multiple times.

I could probably just use the C rand function, but I'd much rather use the C++ implementation so that I can learn more about it... this is an educational exercise at this point.

This is sample the output I get on Windows 10/GCC/DevC++:
46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 58

This is sample output I get on Mac 10.11.6/Clang/Xcode8:
86 37 83 81 1 84 45 90 16 53 49 57 31 90 79 11 76 87 24 74 89 64 97 90 54 61 62 92 95 29 91 87 7 71 85 1 25 4 59 46 12 21 75 7 9 4 61 38 94 14 71 81 13 100 87 97 32 4 62 82 16 56 83 19 45 19 38 82 28 36 63 14 32 53 83 8 22 9 10 91 41 32 50 1 33 21 37 70 98 77 14 99 11 62 65 38 57 14 76 15

Here is the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <chrono>
#include <random>
#include <iomanip>

using namespace std;

template <typename T>
inline T get_random_integer (T min, T max) {
    mt19937_64 generator ((chrono::system_clock::now().time_since_epoch().count()));
    uniform_int_distribution<T> distribution(min, max);
    return distribution(generator);
}

int main(int argc, const char * argv[]) {

    for (int i; i < 100; i++) {
        cout << get_random_integer<int>(0, 100) << " ";
    }
    return 0;
}
Last edited on Oct 11, 2016 at 5:52am
Oct 11, 2016 at 6:01am
Also, anyone know why mt19937_64 is faster then mt19937? It appears it's over twice as fast. Below I timed how long it took to spit out a string of 61,779,600 random ones and zeros.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ time for i in $(seq 100); do ./mt19937_64; done > /dev/null

real	1m57.341s
user	1m56.958s
sys	0m0.377s

$ time for i in $(seq 100); do ./knuth_b; done > /dev/null

real	5m29.218s
user	5m28.454s
sys	0m0.735s

$ time for i in $(seq 100); do ./mt19937; done > /dev/null

real	4m30.836s
user	4m30.188s
sys	0m0.618s

Last edited on Oct 11, 2016 at 6:04am
Oct 11, 2016 at 6:11am
You do reseed on every call to get_random_integer(). How many function calls can you do before the value of chrono::system_clock::now().time_since_epoch().count() changes?
Oct 11, 2016 at 6:44am
I'm not sure, judging by the output from the windows virtual machine I would say 5 because the output number changed 5 times.

When I add the following line in Windows/GCC it starts behaving better:

this_thread::sleep_for(std::chrono::milliseconds(1));

Sample output, Windows/GCC 1 millisecond sleep:
34 75 64 29 61 61 21 77 82 1 21 62 64 22 22 90 1 42 94 41 92 76 5 41 58 69 36 35 55 96 34 29 20 82 96 42 27 88 83 90 46 70 65 80 93 10 78 80 19 7 16 79 82 76 6 5 43 2 30 80 5 39 9 66 22 88 91 93 98 62 59 26 18 35 95 68 89 56 23 29 50 74 7 27 78 90 72 53 79 33 17 68 33 16 72 21 73 57 37 73

Is there a way to detect if the compiler is GCC or if the clock is high resolution so that I can add that chrono sleep statement with and if statement?
Last edited on Oct 11, 2016 at 7:04am
Oct 11, 2016 at 7:06am
> First I tried seeding with random_device and that worked on Mac/Clang and Windows/Visual Studio,
> but it's broke on GCC (both Windows and Linux) because it returns the same deterministic series of numbers.

This behaviour is less than ideal, but not broke (not non-conforming):
std::random_device may be implemented in terms of an implementation-defined pseudo-random number engine if a non-deterministic source (e.g. a hardware device) is not available to the implementation. In this case each std::random_device object may generate the same number sequence. http://en.cppreference.com/w/cpp/numeric/random/random_device


> why mt19937_64 is faster then mt19937?

On the architecture used, to generate a single unsigned integer value, std::mt19937 is typically slightly faster (significantly faster on 32-bit implementations) than std::mt19937_64 .

However, std::uniform_int_distribution needs many unsigned integer values to ensure uniformity.
std::mt19937_64 which generates 64 bit random values can generate the same number of random bits with half the number of calls; it may be faster when used in conjunction with a random number distribution.


> this is an educational exercise at this point.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <random>
#include <type_traits>

template < typename T > inline T get_random_integer( T min, T max ) {

    static_assert( std::is_arithmetic<T>::value, "expected integral or floating point type" ) ;

    static std::mt19937_64 generator ( std::chrono::steady_clock::now().time_since_epoch().count() );

    using uniform_distribution = typename std::conditional< std::is_integral<T>::value,
                                                            std::uniform_int_distribution<T>,
                                                            std::uniform_real_distribution<T> >::type ;
    return uniform_distribution(min, max) (generator);
}
Oct 11, 2016 at 2:31pm
why not let uniform_distribution be static too? It's allowed to maintain internal state across calls.
Oct 11, 2016 at 3:10pm
> why not let uniform_distribution be static too?

With this call signature, min and max may vary across calls.

As written, it should have been called get_random_number instead of get_random_integer - oops!
Topic archived. No new replies allowed.