rango - Orden aleatorio de números en C++ utilizando<random>
generar numeros aleatorios en un arreglo en c++ (4)
Eso es porque el orden de evaluación de esta línea
cout << D1(Generator2) << " " << D1(Generator2) << " " << D1(Generator2) << endl ;
no es lo que piensas
Puedes probarlo con esto:
int f() {
static int i = 0;
return i++;
}
int main() {
cout << f() << " " << f() << " " << f() << endl ;
return 0;
}
Salida: 2 1 0
La orden no está especificada por el estándar de C ++, por lo que el orden podría ser diferente en otros compiladores. Consulte la respuesta de Richard Hodges.
Tengo el siguiente código, que escribí para probar una parte de un programa más grande:
#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() ;
}
Los dos generadores tienen el mismo valor y, por lo tanto, esperaba que la segunda fila en la salida fuera idéntica a la primera. En cambio, el resultado es
1 1 3
1 3 1
El estado de los dos generadores como se imprime en los archivos *.dat
es el mismo. Me preguntaba si podría haber algunos subprocesos múltiples ocultos en la generación de números aleatorios que causan la falta de coincidencia de orden.
Compilé con la versión 5.3.0 de g++
, en Linux, con la bandera -std=c++11
.
Gracias de antemano por tu ayuda.
Estas líneas
cout << D1(Generator) << " " ;
cout << D1(Generator) << " "
<< D1(Generator) << endl ;
cout << D1(Generator2) << " "
<< D1(Generator2) << " "
<< D1(Generator2) << endl ;
porque D1()
devuelve un int, para el cual ostream::operator<<()
tiene una sobrecarga, está llamando efectivamente (excluyendo endl
)
cout.operator<<(D1(Generator));
cout.operator<<(D1(Generator))
.operator<<(D1(Generator));
cout.operator<<(D1(Generator2))
.operator<<(D1(Generator2))
.operator<<(D1(Generator2));
Ahora, el estándar tiene esto para decir,
§ 5.2.2 [4]
Cuando se llama a una función, cada parámetro debe inicializarse con su argumento correspondiente.
[Nota: Tales inicializaciones están secuenciadas indefinidamente una con respecto a la otra - nota final]
Si la función es una función miembro no estática, este parámetro de la función se inicializará con un puntero al objeto de la llamada
Así que vamos a desglosar la expresión anterior
cout.operator<<(a()) // #1
.operator<<(b()) // #2
.operator<<(c()); // #3
Para ilustrar la construcción de this
puntero, estos son conceptualmente equivalentes a (omitiendo ostream::
por brevedad):
operator<<( // #1
&operator<<( // #2
&operator<<( // #3
&cout,
a()
), // end #3
b()
), // end #2
c()
); // end #1
Ahora veamos la llamada de nivel superior. ¿Qué evaluamos primero, #2
o c()
? Dado que, como se destaca en la cita, el orden es indeterminado, entonces no lo sabemos, y esto es cierto de manera recursiva: incluso si evaluamos el n #2
, aún nos enfrentaríamos a la cuestión de si evaluar su interno n #3
o b()
Entonces eso explica con suerte qué está pasando aquí más claramente.
Un ligero cambio en el programa revela lo que sucede:
#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 ;
}
Salida:
4 48 12
12 48 4
Por lo tanto, sus generadores producen resultados iguales, pero el orden en que los argumentos de su línea se calculan en orden diferente.
Pruébalo en línea: http://ideone.com/rsoqDe
x << y
es azúcar sintáctico para una llamada de función al operator<<(x, y)
.
Recordará que el estándar de c ++ no impone ninguna restricción al orden de evaluación de los argumentos de una llamada de función.
Por lo tanto, el compilador puede emitir código que calcule x primero o y primero.
Del estándar: §5 nota 2:
Los operadores pueden estar sobrecargados, es decir, tener un significado cuando se aplica a expresiones de tipo de clase (Cláusula 9) o tipo de enumeración (7.2). Los usos de operadores sobrecargados se transforman en llamadas a funciones como se describe en 13.5 . Los operadores sobrecargados obedecen las reglas para la sintaxis especificada en la Cláusula 5, pero los requisitos del tipo de operando, categoría de valor y orden de evaluación se reemplazan por las reglas para la llamada a función .