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?
srand(): ¿por qué llamarlo solo una vez?
Lipika Deka
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, introduceclock_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
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 srand
lo 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 ellong
distancia. la variable de estadonext
está hecholong
ya que la multiplicación interna y la suma excederán el rango de ununsigned int
. Después de eso, el resultado se escala o modifica dentro del rango especificado anteriormente. Aunque puedes hacer la semillalong
.– 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
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 srand
pero 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.
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.
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.
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 porrand()
.– Rey Zorzal
2 de diciembre de 2017 a las 6:25
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