Порядок случайных чисел в C++ с использованием
у меня есть следующий код, который я написал, чтобы проверить часть большей программы:
#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
файлы одинаковы. Мне было интересно, может ли быть какая-то скрытая многопоточность в генерации случайных чисел, вызывающая порядок несоответствие.
я составил с g++
версия 5.3.0, на Linux, с флагом -std=c++11
.
заранее спасибо за вашу помощь.
4 ответов
x << y
является синтаксическим сахаром для вызова функции operator<<(x, y)
.
вы помните, что стандарт c++ не ограничивает порядок оценки аргументов вызова функции.
таким образом, компилятор может свободно испускать код, который сначала вычисляет x или y.
из стандарта: §5 примечание 2:
операторы могут быть перегружены, то есть заданы значения при применении к выражениям типа класса (предложение 9) или тип перечисления (7.2). использование перегруженных операторов преобразуется в вызовы функций, как описано в 13.5. Перегруженные операторы подчиняются правилам синтаксиса, указанным в пункте 5, но требованиям тип операнда, категория значения,и порядок оценки заменяются правилами для вызова функции.
это потому, что порядок оценки этой строки
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++, поэтому порядок может отличаться от других компиляторов, см. ответ Ричарда Ходжеса.
небольшое изменение в программе показывает, что происходит:
#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 вычисляется в другом порядке.
попробовать его в интернете: 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]
при вызове функции каждый параметр должен инициализируется соответствующим аргументом.
[ Примечание: такие инициализации неопределенно последовательны в отношении друг другу-конец ноты ]
если функция не статическая функция-член, этот параметр функции будет инициализируется указателем на объект вызова
Итак, давайте разберем предыдущее выражение
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
, мы все равно столкнулись бы с вопросом, стоит ли оценивать его внутренний #3
или b()
.
так что, надеюсь, это объясняет, что здесь происходит более четко.