How to let Boost::random and Matlab produce the sa

2019-06-26 03:24发布

问题:

To check my C++ code, I would like to be able to let Boost::Random and Matlab produce the same random numbers.

So for Boost I use the code:

boost::mt19937 var(static_cast<unsigned> (std::time(0)));
boost::uniform_int<> dist(1, 6);
boost::variate_generator<boost::mt19937&, boost::uniform_int<> > die(var, dist);
die.engine().seed(0);     
for(int i = 0; i < 10; ++i) {
    std::cout << die() << " ";
}      
std::cout    << std::endl;

Which produces (every run of the program):
4 4 5 6 4 6 4 6 3 4

And for matlab I use:

RandStream.setDefaultStream(RandStream('mt19937ar','seed',0));
randi(6,1,10)

Which produces (every run of the program):
5 6 1 6 4 1 2 4 6 6

Which is bizarre, since both use the same algorithm, and same seed. What do I miss?

It seems that Python (using numpy) and Matlab seems comparable, in the random uniform numbers: Matlab

RandStream.setDefaultStream(RandStream('mt19937ar','seed',203));rand(1,10)

0.8479 0.1889 0.4506 0.6253 0.9697 0.2078 0.5944 0.9115 0.2457 0.7743

Python: random.seed(203);random.random(10)

array([ 0.84790006, 0.18893843, 0.45060688, 0.62534723, 0.96974765, 0.20780668, 0.59444858, 0.91145688, 0.24568615, 0.77430378])

C++Boost

0.8479 0.667228 0.188938 0.715892 0.450607 0.0790326 0.625347 0.972369 0.969748 0.858771

Which is identical to ever other Python and Matlab value...

回答1:

I have to agree with the other answers, stating that these generators are not "absolute". They may produce different results according to the implementation. I think the simplest solution would be to implement your own generator. It might look daunting (Mersenne twister sure is by the way) but take a look at Xorshift, an extremely simple though powerful one. I copy the C implementation given in the Wikipedia link :

uint32_t xor128(void) {
  static uint32_t x = 123456789;
  static uint32_t y = 362436069;
  static uint32_t z = 521288629;
  static uint32_t w = 88675123;
  uint32_t t;

  t = x ^ (x << 11);
  x = y; y = z; z = w;
  return w = w ^ (w >> 19) ^ (t ^ (t >> 8));
}

To have the same seed, just put any values you want int x,y,z,w (except(0,0,0,0) I believe). You just need to be sure that Matlab and C++ use both 32 bit for these unsigned int.



回答2:

Using the interface like

randi(6,1,10)

will apply some kind of transformation on the raw result of the random generator. This transformation is not trivial in general and Matlab will almost certainly do a different selection step than Boost.

Try comparing raw data streams from the RNGs - chances are they are the same



回答3:

In case this helps anyone interested in the question:

In order to the get the same behavior for the Twister algorithm:

  1. Download the file http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/CODES/mt19937ar.c

  2. Try the following:

    #include <stdint.h>
    
    // mt19937ar.c content..
    
    int main(void)
    {
        int i;
        uint32_t seed = 100;
        init_genrand(seed);
        for (i = 0; i < 100; ++i)
            printf("%.20f\n",genrand_res53());
        return 0;
    }
    
  3. Make sure the same values are generated within matlab:

    RandStream.setGlobalStream( RandStream.create('mt19937ar','seed',100) );
    rand(100,1)
    
  4. randi() seems to be simply ceil( rand()*maxval )



回答4:

Thanks to Fezvez's answer I've written xor128 in matlab:

function [ w, state ] = xor128( state )
%XOR128 implementation of Xorshift
%   https://en.wikipedia.org/wiki/Xorshift
%   A starting state might be [123456789, 362436069, 521288629, 88675123]

  x = state(1);
  y = state(2);
  z = state(3);
  w = state(4);

  % t1 = (x << 11)
  t1 = bitand(bitshift(x,11),hex2dec('ffffffff'));

  % t = x ^ (x << 11)
  t = bitxor(x,t1);

  x = y;
  y = z;
  z = w;

  % t2 = (t ^ (t >> 8))
  t2 = bitxor(t, bitshift(t,-8));

  % t3 = w ^ (w >> 19)
  t3 = bitxor(w, bitshift(w,-19));

  % w = w ^ (w >> 19) ^ (t ^ (t >> 8))
  w = bitxor(t3, t2);

  state = [x y z w];
end

You need to pass state in to xor128 every time you use it. I've written a "tester" function which simply returns a vector with random numbers. I tested 1000 numbers output by this function against values output by cpp with gcc and it is perfect.

function [ v ] = txor( iterations )
%TXOR test xor128, returns vector v of length iterations with random number
% output from xor128

% output
v = zeros(iterations,1);

state = [123456789, 362436069, 521288629, 88675123];


i = 1;

while i <= iterations
    disp(i);

    [t,state] = xor128(state);
    v(i) = t;

    i = i + 1;
end


回答5:

I would be very careful assuming that two different implementations of pseudo random generators (even though based on the same algorithms) produce the same result. There could be that one of the implementations use some sort of tweak, hence producing different results. If you need two equal "random" distributions I suggest you either precalculate a sequence, store and access from both C++ and Matlab or create your own generator. It should be fairly easy to implement MT19937 if you use the pseudocode on Wikipedia.

Take care ensuring that both your Matlab and C++ code runs on the same architecture (that is, both runs on either 32 or 64-bit) - using a 64 bit integer in one implementation and a 32 bit integer in the other will lead to different results.



标签: c++ matlab boost