2008-09-29 15 views
11

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 sean de este estado compartido para que puedan ser determinísticos con respecto a los números previamente generados por Twister Twister.Uso de boost :: random como el RNG para std :: random_shuffle

que hemos probado 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 me da un error de plantilla llamando 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 twister original de mersenne. ¿Lo que da? ¿Hay alguna manera de hacer lo que estoy tratando de hacer sin las variables globales?

+0

Una vez que probarlo, ¿podría publicar el código correcto , para la posteridad? Gracias –

+0

Greg: he revertido su cambio. Nunca tendrá que escapar caracteres HTML en su código, si está dispuesto a usar bloques de código Markdown (sangría cada línea 4 espacios). –

+0

Simplemente resalte el código y haga clic en el botón "010 101". –

Respuesta

11

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 (descargo de responsabilidad: no probado, podría haber otros errores siniestros).

Este requisito se ha relajado en C++ 0x, pero no sé si el cambio se ha implementado en el modo C++ 0x de GCC todavía, y sería muy sorprendidos al encontrar que presente en cualquier otro compilador .

+0

Probado al mover la estructura y funciona. –

+1

Mientras mueve la estructura a nivel global, también puede hacerlo heredar de std :: unary_function también. :-) –

13

En los comentarios, Robert Gould pedido una versión de trabajo 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); 
} 
+0

Dicho sea de paso, así no formato el código (prefiero "foo & bar", no "foo & bar"), pero pensé que debería dejarlo solo, solo para las personas que piensan que tales modificaciones "podrían marcar la diferencia" ". –

+0

Por curiosidad ¿qué beneficio le da heredar de unary_function? Es obvio desde el operador() cuál es la entrada y la salida ... –

+0

Hace que los funtores sean más compostables. es decir, proporciona algunos tiposdef que facilitan la construcción de otros funtores a partir de su functor. Déjame buscar un enlace para ti ... –

5

estoy usando TR1 en lugar de impulso :: azar aquí, pero no debería importar demasiado.

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); 
+0

Esto en realidad no funciona debido a un error de uno por uno. Como está escrito, 'gen' genera enteros de' 0' a 'N' en lugar de' 0' a 'N - 1' como lo requiere' std :: random_shuffle() '. – Spire

+0

@Spire: uniform_int :: operator (motor, N) devuelve un número en [0, N) (es decir, entre 0 y N-1). Así que esto, aunque complicado, en realidad funciona. – baol

1

pensé que valía la pena señalar que esto es ahora bastante sencillo en C++ 11 utilizando sólo la biblioteca estándar:

#include <random> 
#include <algorithm> 

std::random_device rd; 
std::mt19937 randEng(rd()); 
std::shuffle(vec.begin(), vec.end(), randEng); 
Cuestiones relacionadas