El siguiente código está destinado a generar una lista de cinco números pseudoaleatorios en el intervalo [1100]. Siembro 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 usando Microsoft Visual Studio 2013, funciona como se esperaba (ver más abajo). Cuando lo hago en Arch Linux con el compilador g ++, sin embargo, 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.
Salida de ejemplo 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
Para aumentar el misterio, ese primer número se incrementa periódicamente en uno en Linux. Después de obtener los resultados anteriores, esperé unos 30 minutos e intenté nuevamente encontrar que el primer número había cambiado y ahora siempre se generaba como 26. Ha continuado aumentando en 1 periódicamente y ahora está en 32. Parece corresponder con el valor cambiante de time(0)
.
¿Por qué el primer número rara vez cambia entre ejecuciones y luego, cuando lo hace, se incrementa en 1?
El código. Imprime cuidadosamente 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;
}
sizeof(time_t)
vs.sizeof(default_random_engine::result_type)
?default_random_engine
es completamente diferente en esas dos plataformas.Respuestas:
Esto es lo que está pasando:
default_random_engine
en libstdc ++ (la biblioteca estándar de GCC) esminstd_rand0
, que es un motor congruencial lineal simple: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 diferirá en 16807.
El rango de este generador es [1, 2147483646]. La forma en que libstdc ++ lo
uniform_int_distribution
asigna a un número entero en el rango [1, 100] es esencialmente esta: generar un númeron
. Si el número no es mayor que 2147483600, regrese(n - 1) / 21474836 + 1
; de lo contrario, vuelva a intentarlo con un número nuevo.Debería ser fácil ver que en la gran mayoría de los casos, dos
n
s que difieren solo en 16807 producirán el mismo número en [1, 100] bajo este procedimiento. De hecho, uno esperaría que el número generado aumentara en uno aproximadamente cada 21474836/16807 = 1278 segundos o 21,3 minutos, lo que concuerda bastante bien con sus observaciones.MSVC
default_random_engine
esmt19937
, que no tiene este problema.fuente
rand()
% 7 siempre devuelva 0rand()
es algo comprensible (es una mierda heredada sin esperanza). Usar un PRNG de mierda para algo nuevo es imperdonable. Incluso consideraría esto una violación estándar, ya que la norma requiere "proporcionar al menos un comportamiento aceptable del motor para un uso relativamente casual, inexperto y / o ligero". que esta implementación no proporciona ya que falla catastróficamente incluso para casos de uso triviales como surand % 7
ejemplo.rand()
algo comprensible exactamente? ¿Es solo porque a nadie se le habría ocurrido hacerlo?srand
es demasiado pequeña para generar fácilmente semillas únicas. 3) Devuelve un número entero con un límite superior definido por la implementación que la persona que llama tiene que reducir de alguna manera a un número en el rango deseado, que cuando se hace correctamente es más trabajo que escribir un reemplazo con una API sana pararand()
4) Utiliza un estado mutable globalLa
std::default_random_engine
implementación está definida. Utilicestd::mt19937
o en sustd::mt19937_64
lugar.Además,
std::time
y lasctime
funciones no son muy precisas, utilice los tipos definidos en el<chrono>
encabezado en su lugar:fuente
std::random_device
lugar de current_time para sembrar su generador aleatorio. Consulte cualquier ejemplo de referencia de cpp sobre Random.ctime
es 1 segundo. La granularidad de lasstd::chrono
implementaciones está definida por el usuario, por defecto, porstd::high_resolution_clock
(en Visual Studio es un typedef parastd::steady_clock
), nanosegundos, pero puede elegir una medida mucho más pequeña, por lo tanto, mucho más precisa.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 base a esa semilla, los números que se producen son pseudoaleatorios y uniformemente distribuidos. El método 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 la prueba de la vida real, la sal (semilla) se puede crear pidiendo al usuario que mueva el mouse, mezcle el movimiento del mouse con algunas pulsaciones de teclas y agregue una pizca de conteos 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.
Oh, sí, Linux tiene dos generadores de números aleatorios. Uno, el valor predeterminado es módulo de 32 bits y el otro es módulo de 64 bits. Su elección depende de las necesidades de precisión y de la cantidad de tiempo de cálculo que desee consumir para sus pruebas o uso real.
fuente
collection of mouse, keyboard, network and time of day numbers