Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Uncorrelated parallel random seeds with C++ 2011?

Currently, I have a main application in Fortran that need a seed to generate pseudo-random numbers. I would like to run many (many) times this application with completely uncorrelated seeds (and furthermore completely independent pseudo-random numbers chains).

My question is : how to generate the seeds with C++ 2011 ?

like image 755
Vincent Avatar asked Sep 07 '12 14:09

Vincent


2 Answers

In your main thread, extract a single seed (or seed sequence) from a good random source (e.g. from /dev/urandom on Linux). Use that data to seed a single root PRNG. Then use that PRNG to generate seed values for your thread-local PRNGs.

#include <random>
#include <vector>

typedef std::mt19937 rng_type;
std::uniform_int_distribution<rng_type::result_type> udist;

int main()
{
    rng_type rng;

    // seed rng first, and store the result in a log file:
    rng_type::result_type const root_seed = get_seed();
    rng.seed(root_seed);

    // make thread seeds:
    std::vector<rng_type::result_type> seeds(NUMBER_OF_THREADS);
    for (auto & n : seeds) { n = udist(rng); }

    // make threads...
}

The random number engine interface in <random> lets you seed both from a single integer and from a sequence of integers. If you want additional randomness, you can seed the mt19937 from a sequence of several hundred integers.

like image 190
Kerrek SB Avatar answered Sep 17 '22 19:09

Kerrek SB


C++11 provides std::random_device to provide non-deterministic random numbers if a source is available. You'll have to check your implementation to be sure it's good though. libc++ uses /dev/urandom by default. libstdc++ does as well if the macro _GLIBCXX_USE_RANDOM_TR1 is defined. Visual Studio's implementation is unfortunately not non-deterministic. edit: as of VS2012 their implementation uses Windows' cryptography services.

If std::random_device provides access to a non-deterministic source of randomness (typically /dev/urandom uses a cryptographic PRNG) then this should be sufficient for generating independent seeds.

#include <random>

int main() {
    std::random_device r;
    std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
    std::mt19937 engine(seed);

}

Instead of using a single value as a seed, some engines may do better with more seed data. A seed sequence is the alternative provided by the standard. Engines can be seeded with seed sequences, which are objects you load up with any amount of data and which produces seed data based on that.

std::random_device r;
std::vector<std::mt19937> engines;

int engines = 50;
for (int i = 0; i < engines; ++i) {
    std::seed_seq s{r(), r(), r(), r(), r(), r(), r(), r()};
    engines.emplace_back(s);
}

Eight 32-bit values, 256 bits, is enough but if you really want you can use more. Each of the standard engines documents how much data it uses from a seed sequence.

For example, each mt19937 engine will retrieve mt19937::state_size (624) 32-bit values from the seed sequence. The seeds retrieved from the seed sequence aren't the same as the input data, but they're based on that data, so we can use that much random data in the sequence.

std::random_device r;
std::vector<std::uint_least32_t> data;
std::generate_n(back_inserter(data), 624, std::ref(r));

std::seed_seq seed(begin(data), end(data));

std::mt19937 engine(seed); // 'fully' seeded mt19937
like image 27
bames53 Avatar answered Sep 17 '22 19:09

bames53