使用<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
现在我们来看一下顶级调用。 我们首先评估哪个, #2
或c()
? 正如引文中强调的那样,顺序是不确定的,那么我们不知道 – 这是recursion的:即使我们评估了#2
,我们仍然会面临是否评估其内部的#3
或b()
。
所以希望能更清楚地解释这里发生的事情。