8

I am given to believe that random number generators (RNGs) should only be seeded once to ensure that the distribution of results is as intended.

I am writing a Monte Carlo simulation in C++ which consists of a main function ("A") calling another function ("B") several times, where a large quantity of random numbers is generated in B.

Currently, I am doing the following in B:

void B(){ std::array<int, std::mt19937::state_size> seed_data; std::random_device r; std::generate(seed_data.begin(), seed_data.end(), std::ref(r)); std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); //perform warmup std::mt19937 eng(seq); std::uniform_real_distribution<> randU(0,1); double myRandNum = randU(eng); //do stuff with my random number } 

As you can see, I am creating a new random number generator each time I call the function B. This, as far as I can see, is a waste of time - the RNG can still generate a lot more random numbers!

I have experimented with making "eng" extern but this generates an error using g++:

error: ‘eng’ has both ‘extern’ and initializer extern std::mt19937 eng(seq);

How can I make the random number generator "global" so that I can use it many times?

1
  • Why don't you put all the RNG objects you need in a struct and pass it by reference wherever you want it? Commented Nov 26, 2015 at 3:42

2 Answers 2

12

Be careful with one-size-fits-all rules. 'Globals are evil' is one of them. A RNG should be a global object. (Caveat: each thread should get its own RNG!) I tend to wrap mine in a singleton map, but simply seeding and warming one up at the beginning of main() suffices:

std::mt19937 rng; int main() { // (seed global object 'rng' here) rng.dispose(10000); // warm it up 

For your usage scenario (generating multiple RNs per call), you shouldn't have any problem creating a local distribution for each function call.

One other thing: std::random_device is not your friend -- it can throw at any time for all kinds of stupid reasons. Make sure to wrap it up in a try..catch block. Or, and I recommend this, use a platform specific way to get a true random number. (On Windows, use the Crypto API. On everything else, use /dev/urandom/.)

Hope this helps.

Sign up to request clarification or add additional context in comments.

5 Comments

Very helpful, thank you. But how do I seed rng now? I tried (as an example) eng(r); where r is an std::random_device but I get the error "error: no match for call to ‘(std::mt19937 {aka std::mersenne_twister_engine [...cut out because extremely long...] /usr/include/c++/4.9/bits/random.h:546:7: note: candidate expects 0 arguments, 1 provided". How do you seed it?
Use the seed() member function: rng.seed(your_seed_sequence).
why would you need to warm up a random number generator? I have never heard about warming up generators.
@MarineGalantin “Warming up” gets rid of potential undesirable (i.e. predictable) initial state. Here’s more about using MT specifically.
It might be time to update this answer to provide an actually good example of a RNG. IDK if std::random_device is still broken across any major modern compilers either.
1

You shouldn't need to pass anything or declare anything, as the interaction between mt19937 and uniform_real_distribution is through globals.

std::array<int, std::mt19937::state_size> seed_data; std::random_device r; std::generate(seed_data.begin(), seed_data.end(), std::ref(r)); std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); //perform warmup std::mt19937 eng(seq); B() ... void B() { std::uniform_real_distribution<> randU(0,1); ... 

5 Comments

Thanks for your answer. But I want the std::mt19937 to be the object that I pass by reference, and I haven't been able to declare it outside function B because it seems I cannot initialize outside function B ( std::mt1997 eng; ) and then later say inside the function, ( eng(seq); ). How do I get around this?
eng(seq) is calling the constructor. Do you want to reseed or apply a new seed? Then you need to do eng.seed(seq);
I only want to seed once. Then call randU(eng) each time thereafter to get a random number.
So have you tried doing the initialisation in A() and then just using std::uniform_real_distribution<> randU(0,1); in B()?
Seems to me to already be using global objects and you don't actually need to pass anything.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.