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

9 minutos de lectura

avatar de usuario
Lipika Deka

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

  • intente, en un ciclo, llamar a srand y luego a rand

    – Foo Bah

    8 de septiembre de 2011 a las 6:16

  • Véase también Dilbert’s Tour de Contabilidad.

    –Jonathan Leffler

    3 de enero de 2016 a las 0:05

avatar de usuario
Kornelije Petak

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

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

Entonces, para la misma semilla, siempre obtendrás 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 time(NULL)por lo que si siempre configura la semilla antes de tomar el número aleatorio, obtendrá el mismo número siempre que llame al combo srand/rand varias veces en el mismo segundo.

Para evitar este problema, srand se establece solo una vez por aplicación, porque 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, existe 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, luego tendrá que recurrir a otra forma de elegir una seed (a menos que usted acepte la misma secuencia en diferentes instancias de la aplicación). Pero como dije, eso depende del contexto de uso de su aplicación.

Además, es posible que desee intentar aumentar 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);

  • Nota al margen: gettimeofday está obsoleto en POSIX 2008. En su lugar, introduce clock_gettime que puede requerir la vinculación con -lrt. Sin embargo, es posible que aún no esté disponible en muchas plataformas. En Linux esto está bien. En Mac creo que aún no está disponible. En Windows probablemente nunca estará disponible.

    – Shahbaz

    7 abr 2013 a las 10:23

  • t1.tv_usec es un int largo y srand toma como entrada un int sin signo. (Y acabo de encontrarme con un problema en el que marca la diferencia).

    – Jiminion

    26 de abril de 2017 a las 13:12


  • Eso hizo el truco. Al aumentar la precisión, se deshizo de mis duplicados. Gracias muy mucho. Tengo una fecha límite para entregar y esto me salvó el trasero.

    – Beezer

    1 de diciembre de 2020 a las 4:00

avatar de usuario
fóxis

Los números aleatorios son en realidad 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 el siguiente rand llamar para obtener otro número. Debido a que se usa una cierta fórmula para generar estos “números aleatorios”, por lo tanto, se establece un cierto valor de semilla después de cada llamada a rand devolverá el mismo número de la llamada. Por ejemplo srand (1234); rand (); devolverá el mismo valor. Inicializar una vez el estado inicial con el valor semilla generará suficientes números aleatorios ya que no establece el estado interno con srandlo que hace que los números sean más probables de ser aleatorios.

Generalmente usamos el time (NULL) devolvió el valor de los segundos al inicializar el valor inicial. di el srand (time (NULL)); está en un bucle. Luego, 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 un segundo rand la llamada en el bucle devolverá el mismo “número aleatorio”, que no se desea. Inicializarlo una vez al inicio del programa establecerá la semilla una vez, y cada vez rand se llama, se genera un nuevo número y se modifica el estado interno, por lo que la próxima llamada rand devuelve 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 myrand call modificará el estado interno y lo actualizará, y devolverá un número aleatorio. Cada llamada de myrand tendrá una diferente next Por lo tanto, el método devolverá los diferentes números en cada llamada.

mira el mysrand implementación; simplemente establece el valor inicial que pasa a next. Por lo tanto, si establece el next valora lo mismo cada vez antes de llamar rand devolverá el mismo valor aleatorio, debido a que se le aplicó una fórmula idéntica, lo cual no es deseable, ya que la función está hecha para ser aleatoria.

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

  • ¿No quiere decir (semilla larga sin firmar) para el parámetro de mysrand() ?

    – Jiminion

    26 de abril de 2017 a las 13:15

  • @Jiminion Este es un fragmento de código de man srand . El rango es de 0 a 32767 (asumiendo RAND_MAX), que es mucho menor que el long distancia. la variable de estado next está hecho long ya que la multiplicación interna y la suma excederán el rango de un unsigned int. Después de eso, el resultado se escala o modifica dentro del rango especificado anteriormente. Aunque puedes hacer la semilla long.

    – fóxis

    8 de mayo de 2017 a las 10:22


  • Tenga en cuenta que el estándar C también incluye el fragmento de código que se muestra.

    –Jonathan Leffler

    7 de septiembre de 2017 a las 5:15

avatar de usuario
steve cumbre

Respuesta corta: llamar srand() es no 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 cortar una baraja de cartas.

Piensa en esto, de esta manera. rand() reparte de una gran baraja de cartas, y cada vez que lo llama, todo lo que hace es elegir la siguiente carta de la parte superior de la baraja, darle 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. Es un muy gran baraja, sin embargo: típicamente 4,294,967,296 cartas).

Además, cada vez que se ejecuta su programa, se compra un nuevo paquete de cartas en la tienda del juego, y cada nuevo paquete de cartas siempre tiene la misma secuencia. Entonces, a menos que haga algo especial, cada vez que se ejecute su programa, obtendrá exactamente los mismos números “aleatorios” de rand().

Ahora, podrías decir: “Está bien, entonces, ¿cómo barajo la baraja?” Y la respuesta, al menos en lo que respecta a rand y srand están preocupados- es que no hay forma de barajar las cartas.

entonces que hace srand ¿hacer? Basado en la analogía que he estado construyendo aquí, llamando srand(n) es básicamente como decir, “cortar el mazo n cartas desde arriba”. Pero espera, una cosa más: en realidad es comience con otra plataforma nueva y córtela n cartas desde arriba.

Así que si llamas srand(n), rand(), srand(n), rand()…, con el mismo n cada vez, no solo obtendrá una secuencia no muy aleatoria, en realidad obtendrá el mismo número de rand() cada vez. (Probablemente no sea el mismo número que le diste a srandpero el mismo número de vuelta de rand una y otra vez.)

Así que lo mejor que puedes hacer es cortar el mazo una vezes decir, llamar srand() una vez, al comienzo de su programa, con un n eso es razonablemente aleatorio, por lo que comenzará en un lugar aleatorio diferente en el mazo grande cada vez que se ejecute su programa. Con rand()eso es realmente lo mejor que puedes hacer.

[P.S. Yes, I know, in real life, when you buy a brand-new deck of cards it’s typically in order, not in random order. For the analogy here to work, I’m imagining that each deck you buy from the game shop is in a seemingly random order, but the exact same seemingly-random order as every other deck of cards you buy from that same shop. Sort of like the identically shuffled decks of cards they use in bridge tournaments.]


Anexo: para una demostración muy linda del hecho de que para un algoritmo PRNG dado y un valor inicial dado, siempre obtienes la misma secuencia, consulta esta pregunta (que trata sobre Java, no sobre C, pero de todos modos).

  • Excelente explicación Steve.

    – El maestro borracho

    2 de julio de 2019 a las 6:38

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

Por ejemplo podrías hacer:

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

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

avatar de usuario
achora

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

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

Este método hace que su semilla sea casi aleatoria, ya que no hay forma de adivinar a qué hora comenzó su hilo y el pid también será diferente.

avatar de usuario
foo-bah

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

Para probarlo, si haces algo simple como:

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

verá el mismo número impreso 100 veces.

avatar de usuario
imoc

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

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

  • El punto principal es que inicializar con srand() varias veces con la misma semilla dará como resultado valores idénticos devueltos por rand().

    – Rey Zorzal

    2 de diciembre de 2017 a las 6:25

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad