2009-06-10 10 views
5

He visto bastantes recomendaciones para no sembrar generadores de números pseudoaleatorios más de una vez por ejecución, pero nunca acompañados de una explicación exhaustiva. Por supuesto, es fácil ver por qué la (C/C++) siguiente ejemplo no es una buena idea:Problemas con el siembra de un generador de números pseudoaleatorios más de una vez?

int get_rand() { 
    srand(time(NULL)); 
    return rand(); 
} 

ya llamar get_rand varias veces por segundo produce resultados repetidos.

¿Pero no sería el siguiente ejemplo una solución aceptable?

MyRand.h

#ifndef MY_RAND_H 
#define MY_RAND_H 

class MyRand 
{ 
    public: 
    MyRand(); 
    int get_rand() const; 
    private: 
    static unsigned int seed_base; 
}; 

#endif 

MyRand.cpp

#include <ctime> 
#include <cstdlib> 
#include "MyRand.h" 

unsigned int MyRand::seed_base = static_cast<unsigned int>(time(NULL)); 

MyRand::MyRand() 
{ 
    srand(seed_base++); 
} 

int MyRand::get_rand() const 
{ 
    return rand(); 
} 

main.cpp

#include <iostream> 
#include "MyRand.h" 

int main(int argc, char *argv[]) 
{ 
    for (int i = 0; i < 100; i++) 
    { 
    MyRand r; 
    std::cout << r.get_rand() << " "; 
    } 
} 

es decir, aunque el constructor MyRand se llama varias veces en sucesión rápida, cada llamada a srand tiene un parámetro diferente. Obviamente, esto no es seguro para subprocesos, pero tampoco lo es rand.

+0

Podría agregar que el propósito de este ejercicio es aliviar la "carga" de llamar a srand del cliente de 'MyRand', donde' MyRand' podría estar modelando un dado. Pero, por otro lado, si también construimos ruedas de fortuna, lanzamientos de monedas, etc. de la misma manera, obtendremos muchas semillas. – a038c56f

Respuesta

6

Cada vez que llamas a una función de generador de números pseudoaleatorios, el generador toma un cierto estado interno y produce un número pseudoaleatorio y un nuevo estado interno. El algoritmo para transformar el estado interno se elige cuidadosamente para que la salida parezca aleatoria.

Cuando siembras el generador de números aleatorios, básicamente estás configurando este estado interno. Si restablece el estado interno a algún valor predecible, perderá la apariencia de aleatoriedad.

Por ejemplo, un popular, simple RNG es un generador lineal congruente. Números son generados de esta manera:

X[n+1] = (a X[n] + c) mod m 

En este caso, X [n + 1] es a la vez el resultado y el nuevo estado interno.Si la semilla del generador cada vez que como usted sugiere más arriba, obtendrá una secuencia que tiene este aspecto:

{(ab + c) mod m, (a(b+1) + c) mod m, (a(b+2) + c) mod m, ...} 

donde b es su seed_base. Esto no parece aleatorio en absoluto.

+0

Mi ejemplo main.cpp es un poco exagerado con el fin de demostrar la falta del primer error del ejemplo. La instancia de un nuevo objeto para cada llamada a 'get_rand' conducirá a la situación que usted describe arriba, pero eso es solo una programación derrochadora. Suponiendo que el número de instancias de 'MyRand' son pocas en comparación con las llamadas' get_rand', las cosas se ven un poco mejor. – a038c56f

1

Si su semilla es predecible, que está aquí porque está incrementándola, la salida de rand() también será predecible.

Realmente depende de por qué quiere generar números aleatorios, y de qué tan "aleatorio" es un azar aceptable para usted. En su ejemplo, puede evitar duplicados en sucesión rápida, y eso puede ser suficiente para usted. Después de todo, lo que importa es que funcione.

En casi todas las plataformas hay una mejor manera de generar números aleatorios que rand().

1

Bueno, es un procesamiento adicional que no necesita hacerse.

En ese caso, solo llamaría al constructor una vez con una semilla basada en tiempo antes del inicio del ciclo. Eso garantizará resultados aleatorios sin la sobrecarga adicional de cambiar las semillas para cada iteración.

No creo que su método sea más al azar que eso.

0

Puede pensar en la generación de números aleatorios (esto no es estrictamente cierto en cuanto a la implementación, pero sirve como ilustración) como una tabla de valores. Si recuerdas hacer algo de esto en las estadísticas para hacer muestras aleatorias simples, una semilla básicamente te dice en qué fila y columna comenzar en tu gran tabla de números aleatorios. Resembrar una y otra vez es simplemente innecesario ya que podemos suponer que los números ya están distribuidos normalmente.

Simplemente no hay ningún beneficio adicional para sembrar más de una vez ya que esto debería ser lo suficientemente bueno (dependiendo de la aplicación). Si necesita "más" números aleatorios, existen muchos métodos de generación de números aleatorios. Un caso en el que puedo pensar es generar números aleatorios de una manera segura para subprocesos.

Si bien su solución es aceptable, sus números no serán más aleatorios que la siembra una vez, a nivel mundial. srand generalmente no debe pertenecer a un constructor. Si desea admitir números aleatorios, siembre una vez cuando comience el programa y olvídese de ello.

Cuestiones relacionadas