mt19937 is giving me the same number

I'm trying to understand how rng works
but everytime I run my program it gives me the same number.
I can't figure out what I'm doing wrong and I couldn't understand what's going on with the debugger.

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

int main()
{
	
	std::uniform_int_distribution<int> rng_range( 0, 255 );
	std::random_device rd;
	std::mt19937 rng_mt( rd() );
		
	int random_number = rng_range( rng_mt );
	
	printf( "Random number: %i", random_number );
	
	return 0;
}
Last edited on
My guess is that you use some early implementation of std::random_device that isn't random. You might want to use the current time as seed instead.

 
std::mt19937 rng_mt(std::time(nullptr));
Yeah that makes it work pretty well, thanks. Although I'm still curious as to why it doesn't work my way. I'm positive I've used it like that in the past and it used to work. Maybe the compiler? I'm using the one it came with code::blocks GNU GCC Compiler.
Last edited on
closed account (z05DSL3A)
It may be worth checking std::random_device, just for completeness...
1
2
    std::random_device rd;
    std::cout << rd();

Yep rd() is giving me the same value every time

rd: 581869302
Random number: 143
If you are using some port of the GCC compiler for Microsoft OSs, like MingW or MinGW-w64 in W10 or W7, as far as I know std::random_device is not guaranteed to work.
You can initialize std::mt19937 by std::chrono::high_resolution_clock.
^^^ was just about to say the same thing.

Also note that despite the bug existing in MinGW, there are replacements you can drop in:
https://github.com/euloanty/mingw-std-random_device

I haven't tried it, though.

Not sure if this is useful, but if someone wants to be aware of this when making cross-platform code, to perhaps use random_device only if it detects you're not on MinGW, there's #defines that GCC compilers make:

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

int main()
{
    #ifdef __GNUC__
    
        std::cout << "Using some variant of GCC:\n";
        
        #if defined __MINGW32__ && !defined __MINGW64__
            std::cout << "  Using MinGW (32-bit)\n";
        #elif defined __MINGW64__
            std::cout << "  Using MinGW (64-bit)\n";
        #else
            std::cout << "  Using non-MinGW GCC\n";
        #endif
        
    #else
        std::cout << "Using something other than GCC\n";
    #endif
}

C++20 is around the corner and bits of C++11 code still don't function correctly in MinGW :(
Last edited on
closed account (E0p9LyTq)
@Ganado, "easier" way to check if std::random_device works or not is to check its entropy. There is no need to check what the implementation is.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
std::default_random_engine URNG { };
std::random_device         rd;
unsigned                   seed;

// check if the implementation provides a usable random_device
if (0 != rd.entropy())
{
   // it does, it does!!!
   seed = rd();
}
else
{
   // no random_device available, seed using the system clock
   seed = static_cast<unsigned> (std::chrono::system_clock::now().time_since_epoch().count());
}

urng().seed(seed);


cppreference has this to say about entropy:

This function is not fully implemented in some standard libraries. For example, LLVM libc++ always returns zero even though the device is non-deterministic. In comparison, Microsoft Visual C++ implementation always returns 32, and boost.random returns 10.

The entropy of the Linux kernel device /dev/urandom may be obtained using ioctl RNDGETENTCNT - that's what std::random_device::entropy() in GNU libstdc++ uses as of version 8.1

https://en.cppreference.com/w/cpp/numeric/random/random_device/entropy
Last edited on
Alas, another poor sap stung by std::random_device.

The std::random_device is committee crap that managed to make it into the standard in spite of contentions about it. It is utter garbage. By the standard definition of it, there are so many things that can legitimately go wrong with it that I’d literally rather go play with a snake.

The most damning problem is that standard does not require that it actually do anything useful. Meaning, it is perfectly acceptable for it to give you the same number every time.

Alas, I consider that unmeritable in our post-2017 world, where wristwatches have enough computing power to maintain a STRNG.

Hence the reason I wrote the Boost-licensed CSPRNG — a small, easy-to-use library that uses your OS’s CSPRNG to give you random values any way you want it.
https://github.com/Duthomhas/CSPRNG

It is too bad that random number generation is considered such a cryptic art to the degree that few people believe it can be done right, when in fact it is done right, by your OS, all day, every day, for billions of people around the world. std::random_device is an embarrassment to have failed as badly as it does.
closed account (E0p9LyTq)
@Duthomhas,

My idea for a local use random library, not as ambitious as yours by far, derived from a working paper, http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3551.pdf
(Ooops! I posted a link to another working paper, corrected the link.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/* A simple toolkit to help beginners using <random> library an easier task */

#ifndef __RANDOM_TOOLKIT_HPP__
#define __RANDOM_TOOLKIT_HPP__

#include <random>
#include <chrono>
#include <string>

namespace rtk
{
   inline std::default_random_engine& urng()
   {
      static std::default_random_engine URNG { };
      return URNG;
   }

   inline void srand()
   {
      std::random_device rd;
      unsigned           seed;

      // check if the implementation provides a usable random_device
      if (0 != rd.entropy())
      {
         // it does, it does!!!
         seed = rd();
      }
      else
      {
         // no random_device available, seed using the system clock
         seed = static_cast<unsigned> (std::chrono::system_clock::now().time_since_epoch().count());
      }

      urng().seed(seed);
   }

   // two function overloads to obtain uniform distribution ints and doubles
   inline int rand(int from, int to)
   {
      static std::uniform_int_distribution<> dist { };

      return dist(urng(), decltype(dist)::param_type { from, to });
   }

   inline double rand(double from, double to)
   {
      static std::uniform_real_distribution<> dist { };

      return dist(urng(), decltype(dist)::param_type { from, to });
   }
}

#endif 

I might consider simplifying it by eliminating the check if std::random_device works.

I really like your C random code. Shamelessly stolen! :D
Last edited on
I might consider simplifying it by eliminating the check if std::random_device works.

You might as well. The entropy() isn’t required to tell you whether or not TRNG values are present. It says an implementation may tell you about available entropy, but the reality is that available entropy is a meaningless number anyway. For std::random_device, it can legally tell you anything, true or false, and have no meaningful consequence on whether or not you are going to get TRNs from it.

Glad you liked the code. It is licensed using the Boost License, which is probably the friendliest license in existence. Enjoy!


[edit]
When using std::random_device, always wrap the entire effort in a try..catch block.
Last edited on
closed account (E0p9LyTq)
When using std::random_device, always wrap the entire effort in a try..catch block.

I've given up using std::random_device. The unreliability of the thing actually working cross-platform (Visual Studio vs. MinGW, etc.) convinced me to just use std::chrono::system_clock or other <chrono> time keepers as a RNG seed.

That posted iteration of my simple random toolkit is several years old. It shows it.
You could combine multiple seeds by using xor.

1
2
auto seed = std::time(nullptr) ^ std::random_device()();
std::mt19937 rng_mt(seed);

This way you don't need to worry about entropy() and you still get a random seed if random_device is able to provide it, otherwise you fall back on time. Just make sure that the seeds are independent because xoring two identical bit patterns will cancel each out.
Last edited on
Hashing seeds is a common idea taught to people, but a bad one.

See, the problem is that the seed is supposed to be a random index into your random sequence. Same seed == same random numbers.

Unfortunately, there isn’t a whole lot of “random” you can choose from for a seed; the clock is your easiest target. But when you are seeding more often than the clock can provide different numbers (multiple times a second)* people start to look around to figure out what more they can mix into the seed value.

*which, BTW, is a design flaw —you shouldn’t be seeding RNGs a whole bunch of times. Seed one RNG, and use it over and over, even if it is to use it as a seed function for your multiple-times-a-second RNGs.

Common things to mix in are PID or network connection ID and the like —which is to say, they are hashing in stuff that is completely non-random to try to increase randomness. It may appear to work to some degree, but anyone who pays attention to their RNG responses at some point realize something went wrong...

And the common wisdom doesn’t really know how to fix that.

The correct answer is to use a single RNG as your seeds source function, or, better yet, avoid using multiple RNGs altogether and stick to the one.

────────


Entropy has nothing to do with your seed. As simply put as possible, entropy is the idea that your TRNG source has been sufficiently randomized by observed “random” events. More accurately, entropy is a measure of how well-randomized your TRNG is.

For example, when the computer first starts up, there hasn’t been enough time to observe randomness: your TRNG source is not very random*. Only after the computer has been running for a while has enough “entropic events” (random-ish stuff) been observed to make the TRNG source pool sufficiently random.

*and can be observed, and then guessed, which is why bootup is an easy, known point of failure in secure random computing. The way this is overcome is to regularly cache the current random pool, so at bootup the random pool can be reinitialized using its prior value. That, and make it harder for observers to reboot your computer.

────────


Now, in this post alone we have considered two different kinds of random number generators: pseudo-devices whose source pool we need to seed, and true-devices that we let the OS (or other underlying system) manage. (Believe me, this is a simplification.)

To seed a PRNG we need some sufficiently “random” source, such as the system clock —which, as you are probably thinking right now, really isn’t all that random (because it isn’t).

The random quality of the system clock comes from the idea that your PRNG is not regularly seeded, but it IS seeded at a relatively random event, such as whenever the user starts your program up. If the user sets your program to run at exactly 3 AM every day, there is a problem...

However, for most pseudo-random needs, such as video games and the like, it is sufficient.

For things like preparing a crypto document (think HTTPS connection using SSL), it is not. For that you need the system’s CSPRNG. How the CSPRNG works underneath is a whole post in itself that I’ll... not go into right now, but it is sufficient to say that the numbers you pull from it can be considered true noise.

I hope you also see how “entropy” is a relatively useless number, as its ends of measurement are not defined. All you can say is that zero means there is absolutely no entropy in the system, which is definitely not something you want to tell people, and non-zero means there is some degree of entropy in the system, even a possibly useless amount (such as a single event update).

Further, and the problem with std::random_device, user programs are not likely to have access to the OS’s measure of entropy (or at least, they shoudn’t), so the standard merely says it may provide you a number, but acknowledges that any number you get from it might not mean anything for any number of reasons.

Ignore entropy(). Like the rest of std::random_device, it is designed to fail without necessarily making you aware.


And... after writing all this, I need to go and finish writing my random number FAQ.
Topic archived. No new replies allowed.