2008-09-29 9 views
11

J'ai un programme qui utilise le générateur de nombres aléatoires mt19937 de boost :: random. J'ai besoin de faire un random_shuffle et je veux que les nombres aléatoires générés pour cela proviennent de cet état partagé afin qu'ils puissent être déterministes par rapport aux nombres générés précédemment par mersenne twister.Utilisation de boost :: random comme RNG pour std :: random_shuffle

J'ai essayé quelque chose comme ceci:

void foo(std::vector<unsigned> &vec, boost::mt19937 &state) 
{ 
    struct bar { 
     boost::mt19937 &_state; 
     unsigned operator()(unsigned i) { 
      boost::uniform_int<> rng(0, i - 1); 
      return rng(_state); 
     } 
     bar(boost::mt19937 &state) : _state(state) {} 
    } rand(state); 

    std::random_shuffle(vec.begin(), vec.end(), rand); 
} 

mais je reçois une erreur de modèle appelant random_shuffle avec rand. Cependant cela fonctionne:

unsigned bar(unsigned i) 
{ 
    boost::mt19937 no_state; 
    boost::uniform_int<> rng(0, i - 1); 
    return rng(no_state); 
} 
void foo(std::vector<unsigned> &vec, boost::mt19937 &state) 
{ 
    std::random_shuffle(vec.begin(), vec.end(), bar); 
} 

Probablement parce que c'est un appel de fonction réel. Mais évidemment, cela ne garde pas l'état de la twister mersenne originale. Ce qui donne? Y at-il un moyen de faire ce que j'essaye de faire sans variables globales?

+0

Une fois que vous le tester, pourriez-vous s'il vous plaît poster le bon code , pour la postérité? Merci –

+0

Greg: J'ai inversé votre changement. Vous n'avez jamais besoin d'échapper des caractères HTML dans votre code, si vous souhaitez utiliser des blocs de code Markdown (indentez chaque ligne 4 espaces). –

+0

Sélectionnez le code et cliquez sur le bouton "010 101". –

Répondre

11

En C++ 03, vous ne pouvez pas instancier un modèle basé sur un type fonction-local. Si vous déplacez la classe rand hors de la fonction, cela devrait fonctionner correctement (avertissement: non testé, il pourrait y avoir d'autres bugs sinistres).

Cette exigence a été assouplie en C++ 0x, mais je ne sais pas si la modification a été implémentée dans le mode C++ 0x de GCC, et je serais très surpris de la trouver dans tout autre compilateur .

+0

Testé en déplaçant la structure et fonctionne. –

+1

Pendant que vous déplacez la structure au niveau global, n'hésitez pas à lui faire hériter de std :: unary_function aussi. :-) –

13

Dans les commentaires, Robert Gould a demandé une version de travail pour la postérité:

#include <algorithm> 
#include <functional> 
#include <vector> 
#include <boost/random.hpp> 

struct bar : std::unary_function<unsigned, unsigned> { 
    boost::mt19937 &_state; 
    unsigned operator()(unsigned i) { 
     boost::uniform_int<> rng(0, i - 1); 
     return rng(_state); 
    } 
    bar(boost::mt19937 &state) : _state(state) {} 
}; 

void foo(std::vector<unsigned> &vec, boost::mt19937 &state) 
{ 
    bar rand(state); 
    std::random_shuffle(vec.begin(), vec.end(), rand); 
} 
+0

Incidemment, ce n'est pas comme ça que je forme du code (je préfère "foo & bar", pas "foo & bar"), mais je pense que je devrais le laisser tranquille, juste pour les gens qui pensent ". –

+0

Par curiosité quel avantage donne l'héritage d'unary_function? Il est évident d'opérateur() ce que l'entrée et la sortie sont ... –

+0

Cela rend les foncteurs plus composables. c'est-à-dire, il fournit quelques typedefs qui facilitent la construction d'autres foncteurs de votre foncteur. Laissez-moi traquer un lien pour vous .... –

5

J'utilise TR1 au lieu de boost :: ici au hasard, mais ne devrait pas beaucoup d'importance.

Ce qui suit est un peu compliqué, mais cela fonctionne.

#include <algorithm> 
#include <tr1/random> 


std::tr1::mt19937 engine; 
std::tr1::uniform_int<> unigen; 
std::tr1::variate_generator<std::tr1::mt19937, 
          std::tr1::uniform_int<> >gen(engine, unigen); 
std::random_shuffle(vec.begin(), vec.end(), gen); 
+0

Cela ne fonctionne pas réellement en raison d'un erreur hors-un. Comme écrit, 'gen' génère des entiers de' 0 '' N' au lieu de '0'' 'N - 1' comme requis par' std :: random_shuffle() '. – Spire

+0

@Spire: uniform_int :: operator (moteur, N) renvoie un nombre dans [0, N) (c'est-à-dire entre 0 et N-1). Donc, cela, bien que difficile, fonctionne réellement. – baol

1

Je pensais qu'il était intéressant de souligner que c'est maintenant assez simple en C++ 11 en utilisant uniquement la bibliothèque standard:

#include <random> 
#include <algorithm> 

std::random_device rd; 
std::mt19937 randEng(rd()); 
std::shuffle(vec.begin(), vec.end(), randEng); 
Questions connexes