Question

J'ai un programme qui utilise le générateur de nombres aléatoires mt19937 de boost :: random. Je dois faire un random_shuffle et je veux que les nombres aléatoires générés à cet effet proviennent de cet état partagé, de sorte qu'ils puissent être déterministes par rapport aux nombres précédemment générés par le twister de Mersenne.

J'ai essayé quelque chose comme ça:

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 j'obtiens une erreur de modèle lors de l'appel de 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 préserve pas l’état du twister original de Mersenne. Ce qui donne? Existe-t-il un moyen de faire ce que j'essaie de faire sans variables globales?

Était-ce utile?

La solution

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

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

Autres conseils

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

J'utilise tr1 au lieu de boost :: random ici, mais cela ne devrait pas avoir beaucoup d'importance.

Ce qui suit est un peu délicat, 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);

Je pensais que cela valait la peine de souligner que cela 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);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top