如何使用<random>?</random>在多种类型的编译器和内核上生成相同的随机数序列

时间:2015-02-19 13:40:08

标签: c++ c++11 random

问题

我需要在不同的机器和编译器上生成相同的(伪)随机数序列。如果我使用相同的内核,似乎g ++中的mersenne twister(MT)的实现效果很好:无论我在新机器上编译我的程序,使用g ++ 4.9或4.7,我都得到相同的随机数。但是如果我使用较旧的内核或者我改用Visual Studio的编译器,我会得到不同的。没关系,因为没有保证mersenne_twister_engine::seed将内部状态设置为不同的编译器。

我已经尝试了什么

我认为在生成器上应用operator<<会产生一个唯一的结果,可用于在operator>>的其他计算机上设置生成器,但在mt19937的情况下,似乎它不起作用。为了说清楚,在计算机A上我有代码

mt19937 generator1A;
uniform_int_distribution<int> distribution(0, 1000);

cout << "Generating random numbers with seed 1000" << endl;

generator1A.seed(1000);
generator1A(); //to advance the state by one so operator>> will give a longer output; this is not necessary indeed
ofstream test("testseed1000.txt");
test << generator1A << endl;

for (int i = 0; i < 10; ++i)
    cout << distribution(generator1A) << endl;

它产生252,590,893,......和一个长文件。我将文件传输到另一台机器B,并运行以下代码:

mt19937 generator1B, generator2B;
uniform_int_distribution<int> distribution(0, 1000);

cout << "Generating random numbers with seed 1000, and with operator>>" << endl;
generator2B.seed(1000);
generator2B(); // to advance the state by one here as well

ifstream test("testseed1000.txt");

test >> generator1B;
cout << "************************" << endl;
cout << generator1B << endl;
cout << "************************" << endl;
cout << "With seed\twith operator>>" << endl;

for (int i = 0; i < 10; ++i)
    cout << distribution(generator2B) << "\t" << distribution(generator1B) << endl;

它产生

654     205
205     115
115     610

问题

你能给出建议如何在Windows上至少用VC ++和Debian和Ubuntu上的g ++生成相同的(伪)随机数吗?如果可能的话,我想使用std,我不想实现自己的MT引擎。

注意:

  • 创建数百万随机数然后读入不是解决方案
  • 我必须使用MSVS进行代码开发和使用unix服务器进行模拟
  • 除了MT引擎之外,
  • 也欢迎,但我更喜欢MT

2 个答案:

答案 0 :(得分:12)

要从问题中删除变量,请尝试查看随机数引擎的输出,而不是将其输入到分布中。

我刚看了uniform_int_distribution的几个文档页面,并没有说明如何使用统一随机数生成器的输出。我不确定标准是什么。

我预测你 从你的mersenne twister获得相同的输出,但是你将这些输出提供给uniform_int_distribution的两个不等效的实现。

如果这是真的,那么为了获得相同的随机数序列,您将不得不使用分布函数的外部实现来确保在所有系统上获得相同的结果。

答案 1 :(得分:8)

该标准要求引擎通过指定默认构造的引擎必须在第10000次调用时生成的值(它还指定引擎的实际转换和生成算法)来跨实现生成可重现的数字。例如,对于mt19937,标准指定([rand.predef] / p3):

typedef mersenne_twister_engine<uint_fast32_t,
       32,624,397,31,0x9908b0df,11,0xffffffff,7,0x9d2c5680,15,0xefc60000,18,1812433253>
       mt19937;
     

必需行为:类型mt19937的默认构造对象的10000 th 连续调用将产生值   4123659995。

对于分发,没有这样的要求;相反,标准说([rand.dist.general] / p3):

  

用于生成每个指定分布的算法是   实现定义的。

换句话说,实现可能会对分发使用不同的算法,但必须记录它们使用的算法。 MSVC和libstdc ++可能使用不同的算法。

如果您想要完全可移植性,可以考虑使用外部实现,例如Boost.Random的分发类。