Использование boost::random в качестве ГСЧ для std::random_shuffle
У меня есть программа, которая использует генератор случайных чисел mt19937 из boost::random. Мне нужно сделать random_shuffle и хотеть, чтобы случайные числа, сгенерированные для этого, были из этого общего состояния, чтобы они могли быть детерминированными по отношению к ранее сгенерированным числам твистера Мерсенна.
Я попробовал что-то вроде этого:
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);
}
Но я получаю ошибку шаблона, вызывающую random_shuffle с rand. Однако это работает:
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);
}
Вероятно, потому что это фактический вызов функции. Но очевидно, что это не удерживает государство от первоначального закрутки мерсенна. Что дает? Есть ли способ сделать то, что я пытаюсь сделать без глобальных переменных?
4 ответа
В C++03 вы не можете создать экземпляр шаблона на основе функционально-локального типа. Если вы переместите класс rand из функции, он должен работать нормально (отказ от ответственности: не проверено, могут быть другие зловещие ошибки).
Это требование было смягчено в C++0x, но я не знаю, было ли это изменение реализовано в GCC в режиме C++0x, и я был бы очень удивлен, обнаружив его в любом другом компиляторе.
В комментариях Роберт Гулд попросил рабочую версию для потомков:
#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);
}
Я использую tr1 вместо boost::random, но это не должно иметь большого значения.
Ниже немного сложно, но это работает.
#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);
Я подумал, что стоит отметить, что теперь это довольно просто в C++11 с использованием только стандартной библиотеки:
#include <random>
#include <algorithm>
std::random_device rd;
std::mt19937 randEng(rd());
std::shuffle(vec.begin(), vec.end(), randEng);