c++ - randomize - srand en c
Utilidad de `rand()`-¿o quién debería llamar a `srand()`? (4)
Antecedentes: utilizo rand()
, std::rand()
, std::random_shuffle()
y otras funciones en mi código para cálculos científicos. Para poder reproducir mis resultados, siempre especifico explícitamente la semilla aleatoria, y la establezco a través de srand()
. Eso funcionó bien hasta hace poco, cuando descubrí que libxml2 también llamaría a srand()
perezosamente en su primer uso, que fue después de mi llamada srand()
.
Llené un informe de error para libxml2 sobre su llamada a srand srand()
, pero obtuve la respuesta:
Inicialice libxml2 primero y luego. Esa es una llamada perfectamente legal hecha desde una biblioteca. No debe esperar que nadie más llame a
srand()
, y la página man en ningún lado indica que se debe evitar el uso desrand()
varias veces.
Esta es en realidad mi pregunta ahora. Si la política general es que cada lib puede / debería / llamará a srand()
, y puedo / podría también llamarlo aquí y allá, realmente no veo cómo eso puede ser útil en absoluto. ¿O cómo es útil rand()
?
Es por eso que pensé, la política general (no escrita) es que ninguna biblioteca debería llamar a srand()
y la aplicación debería llamarla solo una vez al principio. (No tomo multi-hilos en cuenta. Supongo que en ese caso, de todos modos debería usar algo diferente).
También traté de investigar un poco qué otras bibliotecas realmente llaman srand()
, pero no encontré ninguna. ¿Hay alguno?
Mi solución actual es este código feo:
{
// On the first call to xmlDictCreate,
// libxml2 will initialize some internal randomize system,
// which calls srand(time(NULL)).
// So, do that first call here now, so that we can use our
// own random seed.
xmlDictPtr p = xmlDictCreate();
xmlDictFree(p);
}
srand(my_own_seed);
Probablemente, la única solución limpia sería no usar eso en absoluto y solo usar mi propio generador aleatorio (quizás a través de C ++ 11 <random>
). Pero esa no es realmente la pregunta. La pregunta es, ¿quién debería llamar a srand()
, y si todos lo hacen, ¿cómo es útil rand()
?
Bueno, lo obvio ha sido expresado algunas veces por otros, use los nuevos generadores C ++ 11. Lo estoy replanteando por una razón diferente, sin embargo.
Usas la salida para cálculos científicos , y rand
generalmente implementa un generador bastante pobre (mientras tanto, muchas implementaciones convencionales usan MT19937 que, aparte de la mala recuperación de estado, no es tan malo, pero no tienes garantía para un algoritmo en particular, y al menos un compilador convencional todavía usa un LCG realmente pobre).
No hagas cálculos científicos con un generador pobre. Realmente no importa si tienes cosas como hiperplanos en tus números aleatorios si haces algún juego tonto disparando pajaritos en tu teléfono móvil, pero es muy importante para las simulaciones científicas. Nunca use un generador malo. No lo hagas
Nota importante: std::random_shuffle
(la versión con dos parámetros) en realidad puede llamar a rand
, que es un peligro a tener en cuenta si está usando ese, incluso si utiliza los nuevos generadores C ++ 11 que se encuentran en <random>
.
Sobre el problema real, llamar a srand
dos veces (o incluso más seguido) no es problema. En principio puede llamarlo tantas veces como lo desee, todo lo que hace es cambiar la semilla, y consecuentemente la secuencia pseudoaleatoria que sigue. Me pregunto por qué una biblioteca XML querría llamarlo, pero tienen razón en su respuesta, no es ilegítimo que lo hagan. Pero tampoco importa.
Lo único importante para asegurarse es que o bien no te importa obtener una secuencia pseudoaleatoria particular (es decir, cualquier secuencia funcionará, no estás interesado en reproducir una secuencia exacta), o eres el último en llamar srand
, que anulará cualquier llamada anterior.
Dicho esto, implementar tu propio generador con buenas propiedades estadísticas y un período suficientemente largo en 3-5 líneas de código tampoco es tan difícil, con un poco de cuidado. La principal ventaja (aparte de la velocidad) es que usted controla exactamente dónde está su estado y quién lo modifica.
Es poco probable que alguna vez necesite períodos mucho más largos que 2 128 debido al tiempo prohibitivo que realmente consume tantos números. Una computadora de 3GHz que consume un número cada ciclo funcionará durante 10 21 años en un período de 2 128 , por lo que no hay mucho problema para los seres humanos con un promedio de vida útil. Incluso suponiendo que la supercomputadora en la que ejecuta su simulación es un billón de veces más rápido, sus bisnietos no vivirán para ver el final del período.
En la medida en que los períodos como el 2 19937 que entregan los generadores actuales son realmente ridículos, eso es tratar de mejorar el generador en el extremo equivocado si me preguntan (es mejor asegurarse de que sean estadísticamente firmes y que se recuperen rápidamente). desde el peor de los casos, etc.). Pero, por supuesto, las opiniones pueden diferir aquí.
Este sitio enumera un par de generadores rápidos con implementaciones. Son generadores xorshift combinados con un paso de adición o multiplicación y un retraso pequeño (de 2 a 64 palabras de máquina), que da como resultado generadores rápidos y de alta calidad (también hay un banco de pruebas, y el autor del sitio escribió un par de documentos sobre el tema, también). Estoy usando una modificación de uno de estos (la versión de 2 palabras de 128 bits portada a 64 bits, con cambios triples modificados en consecuencia).
Este problema se aborda en la generación de números aleatorios de C ++ 11, es decir, puede crear una instancia de una clase:
std::default_random_engine e1
que le permite controlar completamente solo los números aleatorios generados a partir del objeto e1
(a diferencia de lo que se usaría en libxml). La regla general sería utilizar una nueva construcción, ya que puede generar sus números aleatorios de forma independiente.
Para abordar sus inquietudes, también creo que sería una mala práctica llamar a srand()
en una biblioteca como libxml. Sin embargo, es más que srand()
y rand()
no están diseñados para usarse en el contexto en el que intentas usarlos; son suficientes cuando solo necesitas algunos números aleatorios, como lo hace libxml. Sin embargo, cuando necesite reproducibilidad y se asegure de ser independiente de los demás, el nuevo encabezado <random>
es el camino a seguir para usted. Entonces, para resumir, no creo que sea una buena práctica por parte de la biblioteca, pero es difícil culparlos por hacerlo. Además, no podía imaginarlos cambiando eso, ya que miles de millones de piezas de software probablemente dependen de ello.
La verdadera respuesta aquí es que si quieres estar seguro de que tu secuencia de números aleatorios no está siendo alterada por el código de otra persona, necesitas un contexto de números aleatorios que sea privado para TU trabajo. Tenga en cuenta que llamar a srand
es solo una pequeña parte de esto. Por ejemplo, si llama a alguna función en alguna otra biblioteca que llame a rand
, también interrumpirá la secuencia de SUS números aleatorios.
En otras palabras, si desea un comportamiento predecible de su código, basado en la generación de números aleatorios, debe estar completamente separado de cualquier otro código que use números aleatorios.
Otros han sugerido usar la generación de números aleatorios C ++ 11, que es una solución.
En Linux y otras bibliotecas compatibles, también puede usar rand_r
, que toma un puntero a un unsigned int
a un seed que se usa para esa secuencia. Entonces, si inicializas esa variable de inicialización y luego la utilizas con todas las llamadas a rand_r
, producirá una secuencia única para tu código. Por supuesto, este sigue siendo el mismo generador de rand
antiguo, solo una semilla separada. La razón principal por la que quiero decir esto es que con bastante facilidad podrías hacer algo como esto:
int myrand()
{
static unsigned int myseed = ... some initialization of your choice ...;
return rand_r(&myseed);
}
y simplemente llame a myrand
lugar de std::rand
(y debería poderse trabajar en el std::random_shuffle
que toma un parámetro de generador aleatorio)
Use el nuevo encabezado <random>
lugar. Permite múltiples instancias de motor, utilizando diferentes algoritmos y, lo que es más importante para usted, semillas independientes.
[edit] Para responder a la parte "útil", rand
genera números aleatorios . Para eso es bueno. Si necesita un control detallado, incluida la reproducibilidad, no solo debe tener una semilla conocida, sino un algoritmo conocido. srand
en el mejor de los casos, te proporciona una semilla fija, así que esa no es una solución completa de todos modos.