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

Random Number Order in C++ using <random>

我有以下代码,我编写它是为了测试一个更大程序的一部分:

#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 文件中打印的两个生成器的状态相同。我在想是不是随机数生成有隐藏的多线程导致顺序不匹配

我在 Linux 上用 g++ 版本 5.3.0 编译,带有标志 -std=c++11

在此先感谢您的帮助。

那是因为这一行的求值顺序

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

不是你想的那样。

你可以用这个来测试它:

int f() {
  static int i = 0;
  return i++;
}

int main() {
  cout << f() << " " << f() << " " << f() << endl ;
  return 0;
}

输出:2 1 0


C++ 标准未指定此顺序,因此其他编译器上的顺序可能不同,请参阅 Richard Hodges 的回答。

x << yoperator<<(x, y).

函数调用的语法糖

您会记得,C++ 标准对函数调用参数的求值顺序没有限制。

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

来自标准:§5 注 2:

Operators can be overloaded, that is, given meaning when applied to expressions of class type (Clause 9) or enumeration type (7.2). Uses of overloaded operators are transformed into function calls as described in 13.5. Overloaded operators obey the rules for syntax specified in Clause 5, but the requirements of operand type, value category, and evaluation order are replaced by the rules for function call.

对程序稍作改动即可揭示发生的情况:

#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() returns 一个整数,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]

When a function is called, each parameter shall be initialized with its corresponding argument.

[ Note: Such initializations are indeterminately sequenced with respect to each other — end note ]

If the function is a non-static member function, the this parameter of the function shall be initialized with a pointer to the object of the call

所以让我们分解前面的表达式

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()?因为,正如引用中强调的那样,顺序是不确定的,所以我们不知道——这在递归中是正确的:即使我们评估 #2,我们仍然会面临是否评估其内部 #3b().

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