Domanda

Ho un programma che utilizza il generatore di numeri casuali mt19937 di boost :: random. Devo fare un random_shuffle e voglio che i numeri casuali generati per questo provengano da questo stato condiviso in modo che possano essere deterministici rispetto ai numeri precedentemente generati dal twister di Mersenne.

Ho provato qualcosa del genere:

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

Ma ricevo un errore nel modello che chiama random_shuffle con rand. Tuttavia, funziona:

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

Probabilmente perché è una vera chiamata di funzione. Ma ovviamente questo non mantiene lo stato dal tornado originale di Mersenne. Cosa dà? Esiste un modo per fare ciò che sto cercando di fare senza le variabili globali?

È stato utile?

Soluzione

In C ++ 03, non è possibile creare un'istanza di un modello basato su un tipo di funzione locale. Se si sposta la classe rand dalla funzione, dovrebbe funzionare correttamente (dichiarazione di non responsabilità: non testata, potrebbero esserci altri bug sinistri).

Questo requisito è stato attenuato in C ++ 0x, ma non so ancora se la modifica sia stata implementata nella modalità C ++ 0x di GCC e sarei molto sorpreso di trovarlo presente in qualsiasi altro compilatore.

Altri suggerimenti

Nei commenti, Robert Gould ha chiesto una versione funzionante per i posteri:

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

Sto usando tr1 invece di boost :: random qui, ma non dovrebbe importare molto.

Quanto segue è un po 'complicato, ma funziona.

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

Ho pensato che valesse la pena sottolineare che questo è ora abbastanza semplice in C ++ 11 usando solo la libreria standard:

#include <random>
#include <algorithm>

std::random_device rd;
std::mt19937 randEng(rd());
std::shuffle(vec.begin(), vec.end(), randEng);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top