c++ - for - visual studio compile linux
<random> genera el mismo nĂºmero en Linux, pero no en Windows (3)
El siguiente código está destinado a generar una lista de cinco números pseudoaleatorios en el intervalo [1,100].
default_random_engine
el
default_random_engine
con
time(0)
, que devuelve la hora del sistema en
tiempo unix
.
Cuando compilo y ejecuto este programa en Windows 7 con Microsoft Visual Studio 2013, funciona como se esperaba (ver más abajo).
Sin embargo, cuando lo hago en Arch Linux con el compilador g ++, se comporta de manera extraña.
En Linux, se generarán 5 números cada vez. Los últimos 4 números serán diferentes en cada ejecución (como suele ser el caso), pero el primer número seguirá siendo el mismo.
Ejemplo de salida de 5 ejecuciones en Windows y Linux:
| Windows: | Linux:
---------------------------------------
Run 1 | 54,01,91,73,68 | 25,38,40,42,21
Run 2 | 46,24,16,93,82 | 25,78,66,80,81
Run 3 | 86,36,33,63,05 | 25,17,93,17,40
Run 4 | 75,79,66,23,84 | 25,70,95,01,54
Run 5 | 64,36,32,44,85 | 25,09,22,38,13
Además del misterio, ese primer número aumenta periódicamente en uno en Linux.
Después de obtener los resultados anteriores, esperé unos 30 minutos e intenté nuevamente para encontrar que el primer número había cambiado y ahora siempre se generaba como un 26. Continuó incrementándose en 1 periódicamente y ahora está en 32. Parece corresponder con el valor cambiante del
time(0)
.
¿Por qué el primer número rara vez cambia entre las ejecuciones y luego, cuando lo hace, aumenta en 1?
El código. Imprime perfectamente los 5 números y la hora del sistema:
#include <iostream>
#include <random>
#include <time.h>
using namespace std;
int main()
{
const int upper_bound = 100;
const int lower_bound = 1;
time_t system_time = time(0);
default_random_engine e(system_time);
uniform_int_distribution<int> u(lower_bound, upper_bound);
cout << ''#'' << ''/t'' << "system time" << endl
<< "-------------------" << endl;
for (int counter = 1; counter <= 5; counter++)
{
int secret = u(e);
cout << secret << ''/t'' << system_time << endl;
}
system("pause");
return 0;
}
El
std::default_random_engine
es la implementación definida.
Utilice
std::mt19937
o
std::mt19937_64
en
std::mt19937_64
lugar.
Además,
std::time
y las funciones
ctime
no son muy precisas, utilice los tipos definidos en el encabezado
<chrono>
lugar:
#include <iostream>
#include <random>
#include <chrono>
int main()
{
const int upper_bound = 100;
const int lower_bound = 1;
auto t = std::chrono::high_resolution_clock::now().time_since_epoch().count();
std::mt19937 e;
e.seed(static_cast<unsigned int>(t)); //Seed engine with timed value.
std::uniform_int_distribution<int> u(lower_bound, upper_bound);
std::cout << ''#'' << ''/t'' << "system time" << std::endl
<< "-------------------" << std::endl;
for (int counter = 1; counter <= 5; counter++)
{
int secret = u(e);
std::cout << secret << ''/t'' << t << std::endl;
}
system("pause");
return 0;
}
En Linux, la función aleatoria no es una función aleatoria en el sentido probabilístico del camino, sino un generador de números pseudoaleatorios. Se sala con una semilla y, en función de esa semilla, los números que se producen son pseudoaleatorios y están distribuidos uniformemente. La forma de Linux tiene la ventaja de que, en el diseño de ciertos experimentos utilizando información de poblaciones, se puede medir la repetición del experimento con ajustes conocidos de la información de entrada. Cuando el programa final está listo para las pruebas de la vida real, se puede crear la sal (semilla) pidiéndole al usuario que mueva el mouse, mezcle el movimiento del mouse con algunas pulsaciones de teclas y agregue una racha de recuentos de microsegundos desde el comienzo de El último encendido.
La semilla de números aleatorios de Windows se obtiene de la colección de números de mouse, teclado, red y hora del día. No es repetible Pero este valor de sal puede restablecerse a una semilla conocida, si como se mencionó anteriormente, uno está involucrado en el diseño de un experimento.
Ah, sí, Linux tiene dos generadores de números aleatorios. Uno, el predeterminado es el módulo de 32 bits, y el otro es el módulo de 64 bits. Su elección depende de las necesidades de precisión y la cantidad de tiempo de cómputo que desea consumir para sus pruebas o uso real.
Esto es lo que está pasando:
-
default_random_engine
en libstdc ++ (biblioteca estándar de GCC) esminstd_rand0
, que es un motor congruencial lineal simple:typedef linear_congruential_engine<uint_fast32_t, 16807, 0, 2147483647> minstd_rand0;
-
La forma en que este motor genera números aleatorios es x i + 1 = (16807x i + 0) mod 2147483647.
-
Por lo tanto, si las semillas son diferentes en 1, la mayoría de las veces el primer número generado será diferente en 16807.
-
El rango de este generador es [1, 2147483646]. La forma en que
uniform_int_distribution
libstdc ++uniform_int_distribution
asigna a un número entero en el rango [1, 100] es esencialmente esto: generar un númeron
. Si el número no es mayor que 2147483600, entonces devuelve(n - 1) / 21474836 + 1
; de lo contrario, intente nuevamente con un nuevo número.Debería ser fácil ver que en la gran mayoría de los casos, dos
n
s que difieren solo en 16807 arrojarán el mismo número en [1, 100] según este procedimiento. De hecho, uno esperaría que el número generado aumente en uno aproximadamente cada 21474836/16807 = 1278 segundos o 21.3 minutos, lo que concuerda bastante bien con sus observaciones.
El
default_random_engine
mt19937
es
mt19937
, que no tiene este problema.