srand () – ¿por qué llamarlo solo una vez?

Esta pregunta se trata de un comentario en esta pregunta. ¿Forma recomendada de inicializar srand? El primer comentario dice que srand() debería llamarse solo UNA VEZ en una aplicación. ¿Por que es esto entonces?

Eso depende de lo que estás tratando de lograr.

La aleatorización se realiza como una función que tiene un valor inicial, es decir, la semilla .

Entonces, para la misma semilla, siempre obtendrá la misma secuencia de valores.

Si intenta establecer la semilla cada vez que necesita un valor aleatorio, y la semilla es el mismo número, siempre obtendrá el mismo valor “aleatorio”.

La semilla generalmente se toma de la hora actual, que son los segundos, como en el time(NULL) , por lo que si siempre configura la semilla antes de tomar el número aleatorio, obtendrá el mismo número siempre y cuando llame al combo srand / rand varias veces en el mismo segundo .

Para evitar este problema, srand se establece solo una vez por aplicación, ya que es dudoso que dos de las instancias de la aplicación se inicialicen en el mismo segundo, por lo que cada instancia tendrá una secuencia diferente de números aleatorios.

Sin embargo, hay una pequeña posibilidad de que ejecute su aplicación (especialmente si es corta, o una herramienta de línea de comandos o algo así) muchas veces en un segundo, entonces tendrá que recurrir a alguna otra forma de elegir un semilla (a menos que la misma secuencia en diferentes instancias de aplicación esté bien por usted). Pero como dije, eso depende del contexto de uso de la aplicación.

Además, es posible que desee intentar boost la precisión a microsegundos (minimizando la posibilidad de la misma semilla), requiere ( sys/time.h ):

 struct timeval t1; gettimeofday(&t1, NULL); srand(t1.tv_usec * t1.tv_sec); 

Los números aleatorios son realmente pseudoaleatorios. Primero se establece una semilla, de la cual cada llamada de rand obtiene un número aleatorio, y modifica el estado interno y este nuevo estado se usa en la próxima llamada rand para obtener otro número. Debido a que se utiliza una determinada fórmula para generar estos “números aleatorios”, establecer un determinado valor de semilla después de cada llamada al rand devolverá el mismo número de la llamada. Por ejemplo, srand (1234); rand (); srand (1234); rand (); devolverá el mismo valor. Inicializando una vez que el estado inicial con el valor inicial generará suficientes números aleatorios ya que no establece el estado interno con srand , lo que hace que los números sean más aleatorios.

En general, utilizamos el valor de segundos devueltos por el time (NULL) al inicializar el valor de inicialización. Diga el srand (time (NULL)); está en un bucle Entonces, el ciclo puede iterar más de una vez en un segundo, por lo tanto, la cantidad de veces que el ciclo itera dentro del ciclo en una segunda llamada en el ciclo devolverá el mismo “número aleatorio”, lo cual no es deseable. Inicializarlo una vez al inicio del progtwig establecerá la semilla una vez, y cada vez que se rand , se generará un nuevo número y se modificará el estado interno, por lo que la siguiente llamada regresará un número que es lo suficientemente aleatorio.

Por ejemplo, este código de http://linux.die.net/man/3/rand :

 static unsigned long next = 1; /* RAND_MAX assumed to be 32767 */ int myrand(void) { next = next * 1103515245 + 12345; return((unsigned)(next/65536) % 32768); } void mysrand(unsigned seed) { next = seed; } 

El estado interno next se declara como global. Cada llamada myrand modificará el estado interno y lo actualizará, y devolverá un número aleatorio. Cada llamada de myrand tendrá un next valor diferente, por lo tanto, el método devolverá los diferentes números en cada llamada.

Mira la implementación mysrand ; simplemente establece el valor inicial al que pasará. Por lo tanto, si establece el next valor siempre antes de llamar a rand , devolverá el mismo valor aleatorio, debido a la fórmula idéntica aplicada en él, que no es deseable, ya que la función está hecha para ser aleatoria.

Pero dependiendo de sus necesidades, puede establecer la semilla en cierto valor para generar la misma “secuencia aleatoria” en cada ejecución, por ejemplo, para un punto de referencia u otros.

La razón es que srand() establece el estado inicial del generador aleatorio, y todos los valores que produce el generador son solo “aleatorios” si no tocas el estado entre ellos.

Por ejemplo, podrías hacer:

 int getRandomValue() { srand(time(0)); return rand(); } 

y luego si llama a esa función repetidamente para que time() devuelva los mismos valores en llamadas adyacentes, solo obtiene el mismo valor generado, eso es por diseño.

Respuesta corta: llamar a srand() no es como “tirar los dados” para el generador de números aleatorios. Tampoco es como barajar una baraja de cartas. En todo caso, es más como simplemente cortar una baraja de cartas.

Piensa en esto, de esta manera. rand() trata de una gran baraja de cartas, y cada vez que la llamas, lo único que hace es coger la siguiente carta de la parte superior de la baraja, darte el valor y devolver esa carta al fondo de la baraja. (Sí, eso significa que la secuencia “aleatoria” se repetirá después de un tiempo. Sin embargo, es una baraja muy grande: típicamente 4,294,967,296 cartas.)

Además, cada vez que se ejecuta su progtwig, se compra un paquete de tarjetas completamente nuevo de la tienda de juegos, y cada paquete de tarjetas nuevo siempre tiene la misma secuencia. Así que a menos que haga algo especial, cada vez que se ejecute su progtwig, obtendrá exactamente los mismos números “aleatorios” de rand() .

Ahora, usted podría decir: “Está bien, entonces, ¿cómo mezclo el mazo?” Y la respuesta es (al menos en lo que se refiere a rand y srand ), no hay forma de barajar el mazo.

Entonces, ¿qué hace srand ? Basado en la analogía que he estado construyendo aquí, llamar a srand(n) es básicamente como decir, “cortar el mazo de naipes desde arriba”. Pero espera, una cosa más: en realidad toma otro mazo nuevo y córtalo desde la parte superior .

Entonces, si llamas a srand(n) , rand() , srand(n) , rand() , …, con el mismo n siempre, no obtendrás una secuencia no muy aleatoria, de hecho, obtiene el mismo número de rand() cada vez. (No necesariamente el mismo número que le srand a srand , pero el mismo número vuelve del rand una y otra vez).

Así que lo mejor que puedes hacer es cortar el mazo una vez , es decir, llamar a srand() una vez, al comienzo de tu progtwig, con una n que sea razonablemente aleatoria, para que comiences en un lugar aleatorio diferente en el gran cubierta cada vez que se ejecuta su progtwig.

[PD: sí, lo sé, en la vida real, cuando compras un mazo de cartas nuevo, normalmente está en orden, no en orden aleatorio. Para que la analogía aquí funcione, imagino que cada mazo que compras en la tienda de juegos está en un orden aparentemente aleatorio, pero con el mismo orden aparentemente aleatorio que cualquier otro mazo de cartas que compres en esa misma tienda. Más o menos como las barajas de barajas idénticamente barajadas que usan en los torneos de bridge.]

srand sembrará el generador de números pseudoaleatorios. Si lo llamas más de una vez, resembrarás el RNG. Y si lo llamas con el mismo argumento, reiniciará la misma secuencia.

Para probarlo, si haces algo simple como:

 #include  #include  int main() { for(int i = 0; i != 100; ++i) { srand(0); printf("%d\n", rand()); } } 

verá el mismo número impreso 100 veces.

Una solución más simple para usar srand() para generar diferentes semillas para instancias de aplicaciones ejecutadas en el mismo segundo es como se ve.

 srand(time(NULL)-getpid()); 

Este método hace que su semilla esté muy cerca del azar, ya que no hay forma de adivinar a qué hora comenzó el hilo y el PID también será diferente.

1 \ Parece que cada vez que rand () se ejecuta, establecerá una nueva semilla para el próximo rand ().

2 \ Si srand () se ejecuta varias veces, el problema es si las dos ejecuciones ocurren en un segundo (el tiempo (NULL) no cambia), el siguiente rand () será el mismo que el rand () inmediatamente después del anterior srand ().