Простая типизированная случайная модель макета в C ++ - PullRequest
2 голосов
/ 14 ноября 2011

C ++ 11 новый Random или Boost.Random действительно классный, мощный и гибкий, но громоздкий в использовании из-за выбора генератора , распределения , посева из обработка состояния (и, в свою очередь, повторный вход и потокобезопасность ) и т. д.

Часто, однако, при создании макетаДля объектов в модульных тестах мы действительно просто хотим простой способ создания случайного объекта определенного типа и не заботимся о конкретных параметрах.Я лично считаю, что в C ++ STL и Boost отсутствует простой и многократно используемый способ сделать это.Мы бы действительно хотели сказать, например,

std::vector<uint32_t> x(10);
some_nice_namespace::randomize(x);

, используя какое-то глобальное состояние и только при необходимости быть более конкретным, например

some_nice_namespace::randomize(x, rng_state);

или даже более конкретным, например

some_nice_namespace::randomize(x, rng(rng_state));

Любой, кто работал как на Matlab, так и на C / C ++, должен быть в курсе этого разрыва в производительности.В какой-нибудь библиотеке C ++ реализована какая-либо из этих идей?Если нет, я сам их реализую и, возможно, добавлю в Boost.

1 Ответ

1 голос
/ 15 ноября 2011

Boost.Random , похоже, не предоставляет класс, который связывает генератор вместе с дистрибутивом. Вы можете создать шаблонный класс функторов, который связывает их вместе.

Boost.Foreach и Boost.Range полезны для написания универсального кода, который работает с любым контейнером, массивом или парой итераторов.

Я придумал следующее, которое позволяет вам кратко рандомизировать контейнер, как только вы определили функтор, который связывает генератор и распределение.

#include <iostream>
#include <vector>
#include <boost/random.hpp>
#include <boost/foreach.hpp>
#include <boost/range/metafunctions.hpp>
#include <boost/range/algorithm/generate.hpp>

namespace rng // some nice namespace
{

//------------------------------------------------------------------------------
// Binds a generator together with a distribution
template <class D, class G>
struct Functor
{
    typedef D Distribution;
    typedef G Generator;
    typedef typename D::result_type result_type;

    Distribution distribution;
    Generator generator;

    explicit Functor(const D& dist = D(), const G& gen = G())
    : distribution(dist), generator(gen) {}

    result_type operator()() {return distribution(generator);}
};

//------------------------------------------------------------------------------
// Randomizes a collection (range) with the given functor
template <class Range, class Functor>
void randomize(Range& range, Functor& functor)
{
    BOOST_FOREACH(typename boost::range_reference<Range>::type x, range)
    {
        x = functor();
    }
}

} // namespace rng

//------------------------------------------------------------------------------
int main()
{
    namespace brnd = boost::random;
    typedef rng::Functor<brnd::uniform_int_distribution<>, brnd::mt19937> Dice;

    // This object could be made global if desired
    Dice dice(Dice::Distribution(1,6));

    std::vector<int> rolls(10);
    rng::randomize(rolls, dice); // Concise one-liner!

    /*  Could also use the following one-liner, but dice would be passed by
        copy and the resulting RNG state would not be retained. */
    // boost::generate(rolls, dice);

    std::cout << "Rolls:\n";
    BOOST_FOREACH(int roll, rolls)
        std::cout << roll << "\n";
}

Используя специализацию шаблонов, вы можете предоставить генераторы по умолчанию для различных числовых типов:

//------------------------------------------------------------------------------
template <typename T>
struct DefaultFunctor
{
    typedef Functor<boost::random::uniform_int_distribution<T>,
                    boost::random::mt19937> Type;
    static T generate() {static Type fn; return fn();}
};

template <>
struct DefaultFunctor<float>
{
    typedef Functor<boost::random::uniform_01<float>,
                    boost::random::mt19937> Type;
    static float generate() {static Type fn; return fn();}
};

template <>
struct DefaultFunctor<double>
{
    typedef Functor<boost::random::uniform_01<double>,
                    boost::random::mt19937> Type;
    static double generate() {static Type fn; return fn();}
};

//------------------------------------------------------------------------------
template <class Range>
void randomize(Range& range)
{
    typedef typename boost::range_value<Range>::type value_type;

    BOOST_FOREACH(typename boost::range_reference<Range>::type x, range)
    {
        x = DefaultFunctor<value_type>::generate();
    }
}

//------------------------------------------------------------------------------
int main()
{
    std::vector<float> noise(10);
    rng::randomize(noise);

    std::cout << "Noise:\n";
    BOOST_FOREACH(float sample, noise)
        std::cout << sample << "\n";
}
...