2010-09-01 5 views
3

pour mon programme J'ai besoin d'entiers pseudo-aléatoires avec différentes plages. Jusqu'à présent, j'ai utilisé la fonction rand() mais elle a ses limites.Encapsulating boost :: random pour la facilité d'utilisation pour remplacer rand()

J'ai trouvé que la bibliothèque boost :: random était un bien meilleur remplacement, mais je ne voulais pas créer de générateurs aléatoires partout.
(J'ai besoin d'entiers aléatoires dans de nombreuses classes, parce que c'est un logiciel de test de stress qui rend chaque décision pseudo-aléatoire (-> un test doit être répétable en définissant le même départ)).

C'est pourquoi j'ai capsuled boost :: random dans ma propre classe.

L'idée est de faciliter l'utilisation de sorte qu'il est presque aussi simple que la méthode rand C++()

#include "boost/shared_ptr.hpp" 
#include "boost/random.hpp" 

class Random{ 
public: 
    typedef boost::shared_ptr<Random> randomPtr; 
    typedef boost::mt19937 randomGeneratorType; 

    static randomPtr Get(){ 
     static randomPtr randomGen(new RandomGenerator()); 
     return randomGen; 
    } 

    void SetSeed(int seed){ 
     randomGenerator.seed(seed); 
    } 

    int Random(int lowerLimit, int upperLimit){ 
    boost::uniform_int<> distribution(lowerLimit, upperLimit); 
    boost::variate_generator< randomGeneratorType&, boost::uniform_int<> > 
    LimitedInt(randomGenerator , distribution); 
    return LimitedInt(); 
    } 

private: 
    // prevent creation of more than one object of the LogManager class 
    // use the Get() method to get a shared_ptr to the object 
    Random(): 
    randomGenerator() //initialize randomGenerator with default constructor 
    {} 

    RandomGenerator(const RandomGenerator& orig){}; 

    randomGeneratorType randomGenerator; 
}; 

La génération d'un nombre aléatoire dans une plage donnée sera désormais aussi facile que

#include "Random.h" 
    Random::Get()->SetSeed(123123); // If you want to make the run repeatable 
    int dice = Random::Get()->Random(1,6); 

question:
y at-il quelque chose de mal avec cette façon de générer des nombres aléatoires?
Grand frais généraux que je n'ai pas reconnu?
Pure Technique de programmation périmée ou périmée?

(je suis encore nouveau pour C++ et que vous souhaitez améliorer mes compétences, et je l'ai trouvé que Stack overflow est le meilleur endroit pour obtenir des conseils de haute qualité)

+0

Une réponse tardive, mais il peut être utile d'examiner ma réponse ici: http://stackoverflow.com/a/10895385/15161 – slashmais

Répondre

3

Joe Gauterin a démontré la question, mais il n'a pas offert aucune solution :)

Le problème avec l'état partagé est l'absence de reentrance: par exemple, exécuter deux fois la même méthode ne fournit pas la même résultat.Ceci est particulièrement critique dans les situations multithread, car l'état global peut ne pas toujours changer au même point dans le programme, conduisant ainsi à des résultats incohérents d'un passage à l'autre. La solution est que chaque simulation devrait avoir son propre "état" et que vous évitiez l'état partagé. Ceci peut être accompli de plusieurs façons: vous pouvez toujours utiliser un état "global" mais le rendre local à un thread, par exemple, ainsi les threads ne marcheront pas sur les autres orteils. La version plus propre consiste cependant à stocker cet état quelque part, et la plus simple est d'avoir une classe Context, instanciée une fois par simulation, et qui est un agrégat de l'état de la simulation (pour la simulation). état large).

Avec cela à l'esprit:

class Context 
{ 
public: 
    typedef boost::mt19937 RandomGeneratorType; 

    void SetSeed(int seed){ 
    rg.seed(seed); 
    } 

    int Next(int lowerLimit, int upperLimit) { 
    boost::uniform_int<> distribution(lowerLimit, upperLimit); 
    boost::variate_generator< randomGeneratorType&, boost::uniform_int<> > 
    LimitedInt(rg, distribution); 
    return LimitedInt(); 
    } 

private: 
    RandomGeneratorType rg; 
}; 

Ensuite, passer le Context par exemple autour de votre simulation, et vous pouvez exécuter autant que vous le souhaitez en parallèle.

+0

Cela a du sens. Merci pour la clarification. – zitroneneis

0

Je dirais que cela ressemble bien - De cette façon, vous pouvez facilement remplacer votre algo de génération aléatoire en cas de besoin.

3

Vous avez essentiellement enveloppé votre générateur dans un singleton, introduisant tous les problèmes que les singletons et les variables globales portent. Par exemple, vous auriez des difficultés à exécuter plusieurs tests de stress en parallèle, car votre implémentation n'est pas sécurisée pour les threads.

Mais le principal problème que je vois est que votre wrapper n'est pas plus simple que d'utiliser boost :: random sans le wrapper.

+0

je l'ai compris de sorte que je devrais écrire tout ce qui est répertorié dans ma méthode Random() pour chaque classe où j'ai besoin de nombres aléatoires et en plus je devrais créer une instance de boost :: mt19937 (ou autre chose) par classe? – zitroneneis

1

Vous pouvez probablement éviter Get(). C'est purement subjectif, pour moi. Je préférerais un mécanisme d'appel comme Random::Seed() et Random::Next() ou Random::Next(min,max). Il n'y a pas trop de fonctions dans Random, donc vous pouvez leur faire toutes les fonctions statiques.

Voici une implémentation simple. Mais gardez à l'esprit que c'est en considérant que vous utilisez ceci dans un environnement mono-thread. Pour un environnement multithread, il est préférable de ne pas l'utiliser comme un singleton.

class Random 
{ 
public: 
    typedef boost::mt19937 RandomGeneratorType; 

    static void Seed(int seed) 
    { 
     s_randGen.seed(seed); 
    } 

    static int NextInt(int min_val, int max_val) 
    { 
     boost::uniform_int<> distribution(min_val, max_val);boost::variate_generator< randomGeneratorType&, boost::uniform_int<> > 
     return LimitedInt(s_randGen , distribution);; 
    } 
private: 
    static RandomGeneratorType s_randGen; 
}; 

Random::RandomGeneratorType Random::s_randGen; 
+0

Cela semble bon. Merci et +1 pour le code ;-) – zitroneneis

+0

Répondre avec 0 avantage, en utilisant un membre 'static' n'est pas mieux que d'utiliser un' static' local: c'est fondamentalement juste une autre implémentation du même Global. –

+0

@Matthieu: Cela a un avantage. Évite quelques appels de fonction (comme obtenir le pointeur de boost :: shared_ptr, et le statique Get()). Et comme je l'ai dit, c'est un problème subjectif. Pour toi, ça a l'air méchant, alors que pour moi c'est tout à fait logique. –

0

Vous pouvez également essayer quelque chose comme créer une entité dans un conteneur et les mélanger au hasard.

void 
GenerateRandomString(vector<string>& container, 
        int size_of_string, 
        unsigned long long num_of_records, 
        int thread_id) 
{ 
    srandom(time(0)); 
    random(); 
    for(unsigned long long int i=0; i < num_of_records; ++i) 
    { 
    stringstream str_stream; 
    str_stream.clear(); 
    str_stream << left << setfill('x') << setw(size_of_string-4); 
    str_stream << num_of_records+i+1 << "-" << thread_id; 
    container.push_back(str_stream.str()); 
    } 
    random_shuffle(container.begin(), container.end()); 
} 
+0

Pouvez-vous éditer votre code pour qu'il soit analysé et affiché correctement par SO? Profitez de la syntaxe markdown. http://daringfireball.net/projects/markdown/syntax – Shaihi

1

Ci-dessous est ma version d'encapsulation:

#include <boost/random.hpp> 
#include <ctime> 


int getRandomIntValue(int min, int max) 
{ 
    static boost::minstd_rand gen((unsigned int)std::time(NULL)); 
    boost::uniform_int<int> dist(min, max); 
    boost::variate_generator< 
     boost::minstd_rand&, 
     boost::uniform_int<int>> combgen(gen, dist); 

    return combgen(); 
} 
+0

L'appeler plusieurs fois en succession rapide (plusieurs appels en une seconde, ce qui n'est pas inhabituel) donnera le même nombre à plusieurs reprises. L'amorçage devra être changé pour la plupart des buts pratiques (ajouter un argument de graine, enrouler dans une classe qui met à jour la graine après chaque appel, etc.). Sinon, une solution très propre. – flies