Pregunta

Tengo un programa que usa el generador de números aleatorios mt19937 de boost :: random. Necesito hacer un random_shuffle y quiero que los números aleatorios generados para esto provengan de este estado compartido para que puedan ser deterministas con respecto a los números previamente generados por el mersenne twister.

Probé algo como esto:

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

Pero recibo un error de plantilla llamando a random_shuffle con rand. Sin embargo esto funciona:

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

Probablemente porque es una llamada de función real. Pero obviamente esto no mantiene el estado del tornado original de Mersenne. ¿Lo que da? ¿Hay alguna forma de hacer lo que estoy tratando de hacer sin las variables globales?

¿Fue útil?

Solución

En C ++ 03, no puede crear una instancia de una plantilla basada en un tipo de función local. Si mueve la clase rand fuera de la función, debería funcionar bien (exención de responsabilidad: no probado, podría haber otros errores siniestros).

Este requisito se ha relajado en C ++ 0x, pero aún no sé si el cambio se ha implementado en el modo C ++ 0x de GCC, y me sorprendería mucho encontrarlo presente en cualquier otro compilador.

Otros consejos

En los comentarios, Robert Gould solicitó una versión funcional para la posteridad:

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

Estoy usando tr1 en lugar de boost :: random aquí, pero no debería importar mucho.

Lo siguiente es un poco complicado, pero funciona.

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

Pensé que valía la pena señalar que ahora es bastante sencillo en C ++ 11 utilizando solo la biblioteca estándar:

#include <random>
#include <algorithm>

std::random_device rd;
std::mt19937 randEng(rd());
std::shuffle(vec.begin(), vec.end(), randEng);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top