简体   繁体   English

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

[英]Random Number Order in C++ using <random>

I have the following code, that I wrote to test a part of a larger program : 我有以下代码,我写的是为了测试更大程序的一部分:

#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() ;
}                                                            

The two generators are seeded with the same value, and therefore I expected the second row in the output to be identical to the first one. 这两个生成器的种子具有相同的值,因此我预计输出中的第二行与第一行相同。 Instead, the output is 相反,输出是

1 1 3
1 3 1

The state of the two generators as printed in the *.dat files is the same. 打印在*.dat文件中的两个生成器的状态是相同的。 I was wondering if there might be some hidden multi-threading in the random number generation causing the order mismatch. 我想知道在随机数生成中是否可能存在一些隐藏的多线程导致顺序不匹配。

I compiled with g++ version 5.3.0, on Linux, with the flag -std=c++11 . 我使用g++版本5.3.0在Linux上编译,标志为-std=c++11

Thanks in advance for your help. 在此先感谢您的帮助。

x << y is syntactic sugar for a function call to operator<<(x, y) . x << y是函数调用operator<<(x, y)语法糖。

You will remember that the c++ standard places no restriction on the order of evaluation of the arguments of a function call. 您将记住,c ++标准对函数调用的参数的评估顺序没有限制。

So the compiler is free to emit code that computes x first or y first. 因此编译器可以自由地发出首先计算x或y的代码。

From the standard: §5 note 2: 从标准:§5注2:

Operators can be overloaded, that is, given meaning when applied to expressions of class type (Clause 9) or enumeration type (7.2). 操作符可以重载,也就是说,当应用于类类型(第9节)或枚举类型(7.2)的表达式时赋予操作符。 Uses of overloaded operators are transformed into function calls as described in 13.5 . 重载运算符的使用将转换为函数调用,如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 . 重载运算符遵循第5章中指定的语法规则,但操作数类型,值类别和评估顺序的要求将替换为函数调用的规则

That's because the order of evaluation of this line 那是因为这条线的评价顺序

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

is not what you think. 不是你的想法。

You can test it with this: 你可以用这个来测试它:

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

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

Output: 2 1 0 输出: 2 1 0


The order is not specified by the C++ standard, so the order could be different on other compilers, please see Richard Hodges' answer. 订单未由C ++标准指定,因此其他编译器的订单可能不同,请参阅Richard Hodges的回答。

A slight change to the program reveals what happens: 程序稍有变化就会发现会发生什么:

#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 ;
}

Output: 输出:

4 48 12
12 48 4

So your Generators produce equal results - but the order the arguments of your cout-line are calculated in different order. 所以你的生成器会产生相同的结果 - 但你的cout-line参数的顺序是按不同的顺序计算的。

Try it online: http://ideone.com/rsoqDe 在线试用: http//ideone.com/rsoqDe

These lines 这些线

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

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

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

because D1() returns an int, for which ostream::operator<<() has an overload, are effectively calling (excluding 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));

Now, the standard has this to say, 现在,标准有这个说,

§ 5.2.2 [4] §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 如果函数是非静态成员函数,则应使用指向调用对象的指针初始化函数的this参数。

So let's break down the preceding expression 所以让我们分解前面的表达式

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

To illustrate the construction of the this pointer, these are conceptually equivalent to (omitting ostream:: for brevity): 为了说明this指针的构造,这些在概念上等同于(省略ostream::为了简洁):

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

Now let's look at the top-level call. 现在让我们来看看顶级电话。 Which do we evaluate first, #2 , or c() ? 我们首先评估哪个, #2还是c() Since, as emphasized in the quote, the order is indeterminate, then we don't know—and this is true recursively: even if we evaluated #2 , we would still face the question of whether to evaluate its internal #3 or b() . 因为正如引文所强调的那样,顺序是不确定的,那么我们不知道 - 这是递归的:即使我们评估#2 ,我们仍然会面临是否评估其内部#3b()

So that hopefully explains what's going on here more clearly. 所以希望能更清楚地解释这里发生的事情。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM