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

How to generate the same random number sequence over multiple types of compilers and kernels with <random>?

本文关键字:随机数序列 lt gt random 种类 类型 编译器 内核      更新时间:2023-10-16

问题

我需要在不同的机器和编译器上生成相同的(伪)随机数序列。如果我使用相同的内核,那么在g++中实现mersene-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;

它产生了252590893。。。,和一个长文件。我将文件传输到另一台机器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 seedtwith 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

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

我只看了uniform_int_distribution的一些文档页面,它没有给出任何关于如何使用统一随机数生成器输出的说明。我不确定标准是怎么说的。

我预测,您从mersenne扭曲器中获得相同的输出,但您将这些输出输入到uniform_int_distribution的两个等价实现中。

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

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

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

所需行为mt19937类型的默认构造对象的第10000次连续调用将产生值4123659995.

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

产生每个指定分布的算法是实现定义。

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

如果你想要完全的可移植性,你可以考虑使用外部实现,比如BoostRandom的发行类。