【问题标题】:How to let Boost::random and Matlab produce the same random numbers如何让 Boost::random 和 Matlab 产生相同的随机数
【发布时间】:2011-04-29 07:58:22
【问题描述】:

为了检查我的 C++ 代码,我希望能够让 Boost::Random 和 Matlab 产生相同的随机数。

所以对于 Boost,我使用代码:

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;

产生(程序的每次运行):
4 4 5 6 4 6 4 6 3 4

对于我使用的 matlab:

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

产生(程序的每次运行):
5 6 1 6 4 1 2 4 6 6

这很奇怪,因为两者都使用相同的算法和相同的种子。 我错过了什么?

似乎 Python(使用 numpy)和 Matlab 似乎具有可比性,在随机统一数方面: 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

这与其他 Python 和 Matlab 值相同...

【问题讨论】:

  • 您为什么不在 C 或 MATLAB 中生成随机流,将其存储在文件中,然后通过两种环境访问它?
  • 我认为你不应该喂 Boost whit time(0)。
  • @ntt,这是一种可能性,但对我来说并不有利,这似乎真的是一个 hack。
  • @ralu,好的,感谢您的提示,但即使没有该初始化结果也完全相同
  • 至少要弄清楚是否多次运行相同的 init 每次都会为每个函数产生相同的结果。据我所知,MT 使用自己的种子生成器。

标签: c++ matlab boost


【解决方案1】:

我必须同意其他答案,指出这些生成器不是“绝对的”。它们可能会根据实现产生不同的结果。我认为最简单的解决方案是实现自己的生成器。它可能看起来令人生畏(Mersenne twister 肯定是顺便说一句),但看看Xorshift,一个非常简单但功能强大的。我复制了维基百科链接中给出的 C 实现:

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));
}

要拥有相同的种子,只需输入您想要的任何值 int x,y,z,w (我相信除了(0,0,0,0))。您只需要确保 Matlab 和 C++ 对这些无符号整数都使用 32 位。

【讨论】:

  • 谢谢,这确实很容易实现。我将使用它进行测试,除非有人知道一种简单的现成方法。干杯托马斯
  • 这似乎是最简单的解决方法。我已经在 Matlab 和 C++ 中实现了该方法(+ 一种用于统一和整数的方法)。基本,但对于基本测试来说绝对足够好。谢谢
【解决方案2】:

使用界面喜欢

randi(6,1,10)

将对随机生成器的原始结果应用某种转换。这种转换 is not trivial in general 和 Matlab 几乎肯定会执行与 Boost 不同的选择步骤。

尝试比较来自 RNG 的原始数据流 - 它们很可能是相同的

【讨论】:

  • 不幸的是,rand 的输出似乎也不同(我不知道如何从 Matlab 获取原始输出)。很可惜。但比我想/希望的要复杂得多。我将切换到另一种方法。
【解决方案3】:

如果这有助于任何对此问题感兴趣的人:

为了让 Twister 算法获得相同的行为:

  1. 下载文件 http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/CODES/mt19937ar.c

  2. 尝试以下方法:

    #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. 确保在 matlab 中生成相同的值:

    RandStream.setGlobalStream( RandStream.create('mt19937ar','seed',100) );
    rand(100,1)
    
  4. randi() 似乎只是ceil( rand()*maxval )

【讨论】:

  • +1 MATLAB internally uses genrand_res53 随机生成介于 0 和 1 之间的浮点数。事实上,此函数调用 genrand_int32 两次(两个 32 位整数)来构建其输出,这解释了为什么 OP 与 Boost 相比,每隔一个值看到相同的数字。使用genrand_res53 而不是genrand_real2 的原因是它提供了更高分辨率(53 位)的数字。 MATLAB、Octave、NumPy 以及可能的其他框架都基于“Nishimura & Matsumoto”的参考实现。
  • 在 MATLAB 中,所有内置的 RNG 函数(randrandnrandi)都是根据 genrand_res53 实现的(至少默认生成器是这样),并且它们共享相同的底层随机流randi 等价于ceil(rand()*maxVal),而randn 在默认的 mt19937ar 生成器之上使用 Ziggurat 变换方法。当然 MATLAB 除了 Mersenne Twister 还有其他 PRNG 方法:mathworks.com/help/matlab/math/…。顺便说一句,在 MATLAB 中设置种子可以简化为:rng(100)
【解决方案4】:

感谢 Fezvez 的回答,我在 matlab 中编写了 xor128:

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

每次使用时都需要将状态传递给 xor128。我编写了一个“测试器”函数,它只返回一个带有随机数的向量。我用 gcc 测试了这个函数输出的 1000 个数字和 cpp 输出的值,结果很完美。

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】:

    假设伪随机生成器的两种不同实现(即使基于相同的算法)产生相同的结果,我会非常小心。可能其中一个实现使用了某种调整,因此产生了不同的结果。如果您需要两个相等的“随机”分布,我建议您预先计算一个序列,从 C++ 和 Matlab 存储和访问,或者创建自己的生成器。如果你使用Wikipedia上的伪代码,实现MT19937应该是相当容易的。

    请注意确保您的 Matlab 和 C++ 代码都在相同的架构上运行(即,都在 32 位或 64 位上运行)——在一个实现中使用 64 位整数,在另一个实现中使用 32 位整数导致不同的结果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-14
      • 1970-01-01
      • 2011-03-11
      相关资源
      最近更新 更多