使用<random>的C ++中的随机数字顺序

我有下面的代码,我写了testing一个更大的程序的一部分:

#include <fstream> #include <random> #include <iostream> using namespace std ; int main() { mt19937_64 Generator(12187) ; mt19937_64 Generator2(12187) ; uniform_int_distribution<int> D1(1,6) ; cout << D1(Generator) << " " ; cout << D1(Generator) << " " << D1(Generator) << endl ; cout << D1(Generator2) << " " << D1(Generator2) << " " << D1(Generator2) << endl ; ofstream g1("g1.dat") ; g1 << Generator ; g1.close() ; ofstream g2("g2.dat") ; g2 << Generator2 ; g2.close() ; } 

两个发电机的种子具有相同的值,因此我预计输出中的第二行与第一行相同。 相反,输出是

 1 1 3 1 3 1 

打印在*.dat文件中的两个生成器的状态是相同的。 我想知道在随机数生成中是否会有一些隐藏的multithreading导致顺序不匹配。

我使用g++ version 5.3.0在Linux上编译了flag -std=c++11

在此先感谢您的帮助。

x << y是对operator<<(x, y)的函数调用的语法糖。

你会记得c ++标准对函数调用参数的求值顺序没有任何限制。

所以编译器可以自由地发出先计算x或y的代码。

从标准:§5注2:

运算符可以被重载,也就是说,在应用于类types(第9章)或枚举types(7.2)的expression式时赋予了意义。 重载运算符的使用被转换为函数调用,如13.5所述 。 重载运算符遵守第5章规定的语法规则,但操作数types,值类别和评估顺序的要求被函数调用规则所取代

那是因为这条线的评价顺序

 cout << D1(Generator2) << " " << D1(Generator2) << " " << D1(Generator2) << endl ; 

不是你所想的

你可以用这个来testing它:

 int f() { static int i = 0; return i++; } int main() { cout << f() << " " << f() << " " << f() << endl ; return 0; } 

输出: 2 1 0


顺序不是由C ++标准规定的,所以其他编译器的顺序可能不同,请参阅Richard Hodges的答案。

程序的一个小小的改变揭示了会发生什么:

 #include <fstream> #include <random> #include <iostream> using namespace std ; int main() { mt19937_64 Generator(12187) ; mt19937_64 Generator2(12187) ; uniform_int_distribution<int> D1(1,100) ; cout << D1(Generator) << " " ; cout << D1(Generator) << " " ; cout << D1(Generator) << endl ; cout << D1(Generator2) << " " << D1(Generator2) << " " << D1(Generator2) << endl ; } 

输出:

 4 48 12 12 48 4 

所以你的生成器产生相同的结果 – 但是你的cout-line的参数顺序是按不同的顺序计算的。

在线试用: http : //ideone.com/rsoqDe

这些线

  cout << D1(Generator) << " " ; cout << D1(Generator) << " " << D1(Generator) << endl ; cout << D1(Generator2) << " " << D1(Generator2) << " " << D1(Generator2) << endl ; 

因为D1()返回一个int,对此, ostream::operator<<()有一个重载,正在有效地调用(不包括endl

 cout.operator<<(D1(Generator)); cout.operator<<(D1(Generator)) .operator<<(D1(Generator)); cout.operator<<(D1(Generator2)) .operator<<(D1(Generator2)) .operator<<(D1(Generator2)); 

现在,标准有这个说法,

§5.2.2 [4]

当一个函数被调用时,每个参数都应该用相应的参数初始化。

[注意:这样的初始化相对于彼此被不确定地sorting – 结束注释]

如果函数是一个非静态的成员函数,函数的这个参数应该用一个指向调用对象的指针来初始化

所以我们来分解一下前面的expression式

 cout.operator<<(a()) // #1 .operator<<(b()) // #2 .operator<<(c()); // #3 

为了说明this指针的构造,这些在概念上相当于(为了简洁,省略了ostream:: :):

 operator<<( // #1 &operator<<( // #2 &operator<<( // #3 &cout, a() ), // end #3 b() ), // end #2 c() ); // end #1 

现在我们来看一下顶级调用。 我们首先评估哪个, #2c() ? 正如引文中强调的那样,顺序是不确定的,那么我们不知道 – 这是recursion的:即使我们评估了#2 ,我们仍然会面临是否评估其内部的#3b()

所以希望能更清楚地解释这里发生的事情。