Why do I get the same sequence for every run with std::random_device with mingw gcc4.8.1?
Asked Answered
P

5

73

I use the following code to test the C++ <random> library.

Why do I get the exact same sequence for every run of the compiled executable? Is rd() deterministic upon compilation? How do I get different output for each run?

GCC 4.8.1 on Windows 7 64bit. Using MinGW distribution from http://nuwen.net/mingw.html.

EDIT: I tested the same piece code with Visual Studio. There is no problem. The outputs are non deterministic. This could be a bug in mingw gcc 4.8.1 that I used.

#include <iostream>
#include <random>
using namespace std;

int main(){
 random_device rd;
 mt19937 mt(rd());
 uniform_int_distribution<int> dist(0,99);
 for (int i = 0; i< 16; ++i){
    cout<<dist(mt)<<" ";
 }
 cout <<endl;
}
Piddle answered 18/9, 2013 at 19:27 Comment(10)
Did you check rd.entropy()?Brott
Thanks Alan, rd.entropy() is zero. is the seed fixed for rd() in this case? what's the proper way to use rd()?Piddle
Platform and compiler please. This should definitely not happen, even with entropy() == 0. If it does, that’s a bug.Cerise
It seems you should seed random_device somehow though it doesn't get parameters!Macaw
@MM. No, that’s not how random_device works.Cerise
Could you make the compiler print the contents of the macro _GLIBCXX_USE_RANDOM_TR1 please? If it’s 0, then it’s using mt19937 with a fixed seed as a fallback.Cerise
Bug is still present in mingw-w64 with gcc 4.9.2Pycnometer
Using MinGW 5.3.0 32bit, seeing the same behavior.Cake
Has anybody tried reporting a bug to GCC so it can be fixed? Or is that too much to ask?Bently
Apparently support for rand_s as an entropy source has been added recently to libstdc++, though it also seems to support RDRAND and RDSEED. I think the default is picked through preprocessor definitions during library comping, and possibly overridden by a token string passed through the constructor. Honestly, I find reading the libstdc++ code to be a headache. I cannot tell if rand_s would be the default on Windows (as it should have always been!)Fari
W
36

From http://en.cppreference.com/w/cpp/numeric/random/random_device:

Note that std::random_device may be implemented in terms of a pseudo-random number engine if a non-deterministic source (e.g. a hardware device) is not available to the implementation.

I would expect a decent implementation to at least seed the RNG though.

Edit: I suspect they deliberately chose to deliver the same sequence each time, to make obvious the fact that the stream wasn't as random as promised.

Wagonette answered 18/9, 2013 at 19:28 Comment(3)
I agree. The fallback implementation of stdlibc++ uses a constant seed, which doesn’t strike me as all that smart (and it’s not explained).Cerise
The real failure is to have this pseudo-random fallback in the first place.Evite
@ypnos: the standard has to do something to cover the case of a C++ implementation on a deterministic platform. But doing that on a real platform is a huge quality-of-implementation issue. See also How to succinctly, portably, and thoroughly seed the mt19937 PRNG?.Bulahbulawayo
P
26

I got a confirmed answer from STL from MSFT:

Unlike VC, GCC hasn't implemented random_device nondeterministically on Windows. Boost has, so you can use Boost.Random.

Piddle answered 20/9, 2013 at 2:43 Comment(1)
I have yet to find an appropriate doc indicating which boost lib -l's and what order boosts -l's are needed in gcc to avoid link errors, and any system -l also required. Just adding -lboost-random-mgw48-mt-d-1_57 resulted in the linker complaing there was no boost::random::random_device() and no boost::random::~random_device()Aleen
L
6

This is a GCC bug, fixed in GCC 9.2.

If you have this problem, update your compiler. (You can get a fresh GCC from MSYS2, for example.)

Lacker answered 19/6, 2020 at 21:15 Comment(3)
@northerner Run pacman -Syuu. There's a change that it will close the terminal (and all msys2 programs) to proceed; an older version will ask you to do it manually. If any of that happened, you have to restart MSYS2 and run the same command again to complete the update.Lacker
The update fails #63202480 Also I already have GCC 9.1 and random_device still doesn't work.Raspings
@Raspings Well, as I said it was fixed in GCC 9.2, and your version is older than that. Try reinstalling MSYS2.Lacker
T
4

You may need to pass a parameter to the constructor:

https://gcc.gnu.org/onlinedocs/gcc-4.9.1/libstdc++/api/a00899.html

Translocate answered 16/9, 2014 at 5:42 Comment(0)
I
2
  1. GCC does not implement rd.entropy() correctly - it always returns 0 (at least on Mac OS X).

  2. Unfortunately, there seems to be no way to mix additional entropy into random_device, which matters because it usually/often (look at Linux /dev/random and /dev/urandom, and at the Intel RDRAND implementation) implements a pseudo-random number generator under the hood. I'd like to be able to improve its output by injecting something I consider random to mix with whatever its entropy source produces. Again, since this device (or kernel module) internally implements a cryptographic algorithm for processing the entropy bits it obtains to generate its output, I'd like to be able to "randomize" that process more by injecting my own data to mix with whatever entropy that device picks. For example, consider Java SecureRandom(). It does not allow you to set the seed (which indeed would convert it to PRNG), but it would happily mix what you provide with whatever it is using to "randomize" its output even more.

  3. I personally prefer RDRAND. A small assembly library with a compact C interface. Here are the references:

    David Johnson from Intel explains RDRAND on Stackoverflow

    Stackoverflow pointers to RDRAND library source for Windows, Linux, and Mac OS X

    Intel blog on RDRAND library, and a download link

Incunabula answered 9/4, 2014 at 2:15 Comment(10)
It does not make sense to seed random_device. If it requires a seed, then it is a pseudo-random generator, not a true random number generator, which is what random_device is supposed to be.Corrida
"Seed" was an unfortunate term. You do not "seed" a "true" random_device. But since random devices such as provided by Linux (and even RDRAND that's firmware-implemented) involve software algorithms between their entropy sources and their output available to users, mixing in randomness/entropy from other sources can't hurt the outcome, and sometimes can improve it. I think you should retract your downvote, if you are honest.Incunabula
I think the answer is confusingly written, if you rewrite it then i might retract my downvote. There is a difference between pseudorandom generation and randomness extraction. Theoretically, it works like this. Given a weakly random source, e.g. maybe 1000 bits with only 100 bits of entropy in them, first you want to use an extractor to get ~ 50 bits with ~ 50 bits of entropy in them. In practice this uses a cryptographic hash function. Then the result can be used as a seed to a generator, which stretches the 50 bits to many many more bits for use in your application. (Numbers made up.)Corrida
In C++, if I had an additional source of entropy which I wanted to combine with /dev/random or /dev/urandom, you could write your own bits to /dev/random to supply more entropy to the pool. Also, for simplicity, I would consider just xoring it in with the random results which are read from those channels, which will be at least a mildly efficient way of combining them and won't hurt anything. IMO these things are different from "seeding" /dev/random and this terminology could confuse the reader. Not convinced that the Java API is a good model here.Corrida
Nice. I was not aware that you could write to /dev/random. Now I know better: add a file as entropy source and why writing to /dev/random.... XORing the additional entropy into the result is a good and efficient way - with the disadvantage of being explicit.Incunabula
I think the Java model is the best because it allows the rest of the program that utilizes SecureRandom to be unaware of all those details and improvements, and simply use the standard interface of standard class as-is. Another advantage of the Java SecureRandom API is that it is resistant to corruption by "bad" randomness input by the user. The SecureRandom output can only improve (or stay at the same quality) with additional input.Incunabula
But the C++ way allows for more flexibility. The user can choose from various distributors and generators depending on their memory, performance and randomness needAmbages
While I love C++'s flexibility, the defaults really need to actually be, you know, random.Writeoff
Linux's /dev/urandom is not just a PRNG. It collects entropy from the timing of interrupts, and stuff like that. It does exactly what you describe for SecureRandom: mixing questionable entropy sources, making things better or at least not worse.Bulahbulawayo
Intel's rdrand is also not just a PRNG. It's a PRNG reseeded from true hardware randomness extremely fast. rdseed is true HW randomness (conditioned by running it through AES): it blocks if there isn't enough real entropy. See also software.intel.com/en-us/articles/… for Intel's details on it, and #45069719 for ideas on seeding C++ PRNGs safely.Bulahbulawayo

© 2022 - 2024 — McMap. All rights reserved.