Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to initialize boost::mt19937 with multiple values without using C++11

Tags:

c++

boost

I can use:

boost::mt19937 gen(43);   

this works just fine, but what if I want more than 32-bits of seed before using the random number generator? Is there an easy way to put 64-bits or 128-bits of seed into the Mersenne Twister?

I found a few examples of loading multiple values before generating results, but none of the code works.

There are a couple of problems with this code:

std::vector<unsigned int> seedv(1000, 11);
std::vector<unsigned int>::iterator i=seedv.begin();
boost::mt19937 gen2(i, seedv.end());

First, calling gen2() always returns the same value. I don't know how I screwed that up.

Second, I don't want 1,000 seeds, but when I lower it to 600 it "throws an instance of std::invalid_argument with note enough elements in call to seed"

Can this method be shortened to a handful of seeds?

Here is another code example that looks easy:

std::string seedv("thisistheseed");
std::seed_seq q(seedv.begin(),seedv.end());
boost::mt19937 gen2(q);

but it won't compile. I finally figured out that std::seed_seq is only available in c++11. I am stuck with gcc 4.7 until the libraries I depend on are stable.

I suppose I can just stick with a 32-bit seed, but I wanted a little bit more.

I did read this article: Boost Mersenne Twister: how to seed with more than one value?

I like the idea of initializing the whole vector from:

mersenne_twister(seed1) ^ mersenne_twister(seed2)

but I don't see a way to do that without modifying Mersenne_Twister.hpp

Any suggestions?

UPDATE: one more way not to do it!

unsigned long seedv[4];
seedv[0]=1;
seedv[1]=2;
seedv[2]=3;
seedv[3]=4;
boost::mt19937 gen2(seedv,4);

With the right casting, this should work, but every cast I have tried still won't get past the compiler. I can cast anything in C, but C++ still stumps me at times...

like image 588
user3920315 Avatar asked Aug 07 '14 23:08

user3920315


1 Answers

Use boost::seed_seq instead of std::seed_seq.

Update: Use

boost::random_device rd;
boost::mt19937 eng(rd);

boost::mt19937 allows you to seed it with either a single value up to 32 bits in width (the w parameter of the underlying boost::mersenne_twister_engine), or with a sequence of 624 values (the n parameter of the underlying template). 624 is the number of elements in mt19937's internal state.

If you read the documentation you'll see that these two mechanisms seed the state of the engine differently.

  • seeding with a single value sets every element of the engine's state using a complicated function of that single value.
  • seeding with a sequence of 624 values sets each element of the engine state to exactly the corresponding seed value.

The point is that boost::mt19937 does not itself include a mechanism to map an arbitrary number of seed values to the fixed number of elements in its internal state. It allows you to set the state directly (using 624 values), and for convenience it offers a built-in mapping from single 32-bit values to complete, 624 element states.


If you want to use an arbitrary number of input seed values then you will need to implement a mapping from arbitrarily sized sequences to 624 element states.

Keep in mind that the mapping should be designed such that the resulting internal state is a 'good' state for the mersenne twister algorithm. This algorithm is shift-register based and can be subject to internal states which produce relatively predictable output. The built-in mapping for single values was designed to minimize this issue and whatever you implement should be as well.

Probably the best way to implement such a mapping, rather than doing the mathematical analysis yourself, is to simply use the standard mersenne twister warmup algorithm. According to a comment on this answer the C++11 std::seed_seq is specified to perform such a warmup. Boost includes boost::seed_seq, which presumably does the same thing.


Update: Instead of using some arbitrary number of values to compute a sequence of 624 values you can simply use exactly 624 random values. If the values are unbiased and evenly distributed over the range of 32-bit values then you won't need any warm-up (well, unless you're astronomically unlucky).

The boost <random> library supports this very directly:

boost::random_device rd;
boost::mt19937 eng(rd);

Note that the C++11 <random> library does not support seeding this way.

like image 110
bames53 Avatar answered Sep 22 '22 01:09

bames53