2009-06-10 9 views
5

J'ai vu quelques recommandations pour ne pas ensemencer des générateurs de nombres pseudo-aléatoires plus d'une fois par exécution, mais jamais accompagné d'une explication complète. Bien sûr, il est facile de voir pourquoi l'exemple suivant (C/C++) n'est pas une bonne idée:Problèmes avec l'ensemencement d'un générateur de nombres pseudo-aléatoires plus d'une fois?

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

depuis l'appel get_rand plusieurs fois par seconde produit des résultats répétés.

Mais ne l'exemple suivant encore une solution acceptable?

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() << " "; 
    } 
} 

, c'est-à-dire même si le constructeur MyRand: s est appelé plusieurs fois en succession rapide, chaque appel à srand a un paramètre différent. Évidemment, ce n'est pas sûr pour les threads, mais là non plus, ce n'est pas rand.

+0

Je pourrais ajouter que le but de cet exercice est de soulager le «fardeau» d'appeler srand du client de 'MyRand', où' MyRand' pourrait être en train de modéliser un dé. Mais d'un autre côté, si nous construisons aussi des roues de fortune, des lancers de pièces etc. de la même manière, nous aurons beaucoup de graines. – a038c56f

Répondre

6

Chaque fois que vous appelez un numéro pseudo-aléatoire fonction de générateur, le générateur prend un certain état interne et produit un nombre pseudo-aléatoire et un nouvel état interne. L'algorithme de transformation de l'état interne est soigneusement choisi pour que la sortie apparaisse aléatoire.

Lorsque vous créez le générateur de nombres aléatoires, vous définissez cet état interne. Si vous réinitialisez l'état interne à une valeur prévisible, vous perdrez l'apparence de l'aléatoire. Par exemple, un RNG simple et populaire est un générateur linéaire congruentiel. Les nombres sont générés comme suit:

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

Dans ce cas, X [n + 1] est à la fois le résultat et le nouvel état interne.Si vous semez le générateur chaque fois que vous suggérez ci-dessus, vous obtiendrez une séquence qui ressemble à ceci:

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

où b est votre seed_base. Cela ne semble pas aléatoire du tout.

+0

Mon exemple main.cpp est un peu exagéré dans le but de démontrer l'absence du défaut du premier exemple. Instancier un nouvel objet pour chaque appel 'get_rand' mènera à la situation que vous décrivez ci-dessus, mais ce n'est qu'une programmation inutile. En supposant que le nombre d'instanciations 'MyRand' est peu comparé aux appels' get_rand', les choses semblent un peu mieux. – a038c56f

1

Si votre graine est prévisible, ce qui est ici depuis que vous êtes juste incrémenter, la sortie de rand() sera également prévisible.

Cela dépend vraiment pourquoi vous voulez générer des nombres aléatoires, et comment « au hasard » est un hasard pour vous acceptable. Dans votre exemple, il peut éviter les doublons en succession rapide, et cela peut être assez bon pour vous. Après tout, ce qui compte, c'est que ça fonctionne.

Sur presque toutes les plates-formes, il existe un meilleur moyen de générer des nombres aléatoires que rand().

1

Eh bien c'est un traitement supplémentaire qui n'a pas besoin d'être fait.

Dans ce scénario que je venais appeler le constructeur une fois avec une graine en fonction du temps avant le début de la boucle. Cela garantira des résultats aléatoires sans les frais supplémentaires de changement de graines pour chaque itération.

Je ne pense pas que votre méthode est plus aléatoire que cela.

0

Vous pouvez penser à la génération de nombres aléatoires (ceci n'est plus strictement vrai pour l'implémentation, mais sert d'illustration) comme table de valeurs. Si vous vous rappelez de faire des choses dans les statistiques pour faire des échantillons aléatoires simples, une graine vous indique en gros quelle rangée et quelle colonne commencer dans votre grande table de nombres aléatoires. Rétablir encore et encore est tout simplement inutile puisque nous pouvons déjà supposer que les numéros sont déjà distribués normalement.

Il n'y a simplement aucun avantage supplémentaire à semer plus d'une fois puisque cela devrait être assez bon (selon l'application). Si vous avez besoin de "plus" nombres aléatoires, il existe de nombreuses méthodes de génération de nombres aléatoires. Un cas que je peux penser est de générer des nombres aléatoires d'une manière thread-safe.

Alors que votre solution est acceptable, vos numéros ne seront pas plus aléatoires que de les semer une fois, globalement. srand ne devrait généralement pas appartenir à un constructeur. Si vous souhaitez prendre en charge des nombres aléatoires, semez une fois lorsque le programme démarre et oubliez-le.

Questions connexes