Using boost::random and getting same sequence of n

2019-01-26 21:54发布

问题:

I have the following code:

Class B {

void generator()
{
    // creating random number generator
    boost::mt19937 randgen(static_cast<unsigned int>(std::time(0)));
    boost::normal_distribution<float> noise(0,1);
    boost::variate_generator<boost::mt19937, 
        boost::normal_distribution<float> > nD(randgen, noise);


    for (int i = 0; i < 100; i++)
    {
        value = nD();
        // graph each value
    }
}
};

Class A {

void someFunction()
{
    for(int i = 1; i <=3; i++)
    {
        std::shared_ptr<B> b;
        b.reset(new B());
        b->generator();
    }
}
};

I wish to execute the above code multiple times in rapid succession to produce multiple graphs. I have also reviewed this stackoverflow question which is similar but the caveat states that when time(0) is used and the member function is called in rapid succession then you will still likely get the same sequence of numbers.

How might I overcome this problem?

EDIT: I've tried making randgen static in Class B, also tried making it a global variable in Class A, but each time the 3 graphs are still the same. I've also tried seeding from the GetSystemTime milliseconds. I must be missing something.

回答1:

One way would be to not reseed the random number generator every time you execute your code.

Create the generator and seed it once, then just continue to use it.

That's assuming you're calling that code multiple times within the same run. If you're doing multiple runs (but still within the same second), you can use another differing property such as the process ID to change the seed.

Alternatively, you can go platform-dependent, using either the Windows GetSystemTime() returning a SYSTEMTIME structure with one of its elements being milliseconds, or the Linux getTimeOfDay returning number of microseconds since the epoch.

Windows:

#include <windows.h>
SYSTEMTIME st;
GetSystemTime (&st);
// Use st.wSecond * 100 + st.wMillisecs to seed (0 thru 59999).

Linux:

#include <sys/time.h>
struct timeval tv;
gettimeofday (&tv, NULL);
// Use tv.tv_sec * 100 + (tv.tv_usec / 1000) to seed (0 thru 59999).


回答2:

With Boost.Random you can save the state of the random number generator--for example, you can save it to a text file. This is done with streams.

For example, using your code, after you seed the generator and have run it once, you can save the state with an output stream, like so:

std::ofstream generator_state_file("rng.saved");
generator_state_file << randgen;

Then later, when you've created a new generator, you can load the state back from that file using the opposite stream:

std::ifstream generator_state_file("rng.saved");
generator_state_file >> randgen;

And then use the state to generate some more random numbers, and then re-save the state, and so on and so on.

It may also be possible to save the state to a std::string using std::stringstream, if you don't want to use a file, but I haven't personally tried this.



回答3:

Only create a single random number generator so it's only seeded once:

static boost::mt19937 randgen(static_cast<unsigned int>(std::time(0)));


回答4:

First Thoughts

On unix you could try reading some bytes from /dev/random or /dev/urandom for the seed. You could also try using a combination of time(0) + pid + static counter (or pseudo-random sequence).

I believe on windows, you can use QueryPerformanceCounter to get the value of the high performance timer register.

Another thought:

You could declare your mt19937 prng as a static or global so you never lose its state.

A third thought:

You wish to "execute the above code multiple times in rapid succession to produce multiple graphs" pass in a graph index. (e.g. genGraph(int graphIndex) and combine this (add, xor, etc) with the output of time(0). boost::mt19937 randgen(static_cast<unsigned int>(std::time(0) + graphIndex));



回答5:

A late answer: two random-number generator functions for comparing boost with standard method.

boost

#include <boost/random.hpp>

//the code that uses boost is massively non-intuitive, complex and obfuscated

bool _boost_seeded_=false;

/*--------------------*/int
boostrand(int High, int Low)
{
    static boost::mt19937 random;
    if (!_boost_seeded_)
    {
        random = boost::mt19937(time(0));
        _boost_seeded_=true;
    }
    boost::uniform_int<> range(Low,High);
    boost::variate_generator<boost::mt19937&, boost::uniform_int<> > 
        getrandom(random, range);

    return getrandom();
}

standard

#include <cstdlib>
#include <time.h>

//standard code is straight-forward and quite understandable

bool _stdrand_seeded_=false;

/*--------------------*/int
stdrand(int High, int Low)
{
    if (!_stdrand_seeded_)
    {
        srand(time(0));
        _stdrand_seeded_=true;
    }
    return ((rand() % (High - Low + 1)) + Low);
}

The results from both functions are comparably of the same "randomness". I would apply the KISS-principle.



回答6:

If you do not want to use only one generator you could create one generator with seed(time(0)) and then use that generator as seed into the other generators.

time(0) has the resolution of 1 second. Using it multiple times as seed within a short time span will create the same generator.



标签: c++ random