Frage

C++11's new Random or Boost.Random is really cool, powerful and flexible but cumbersome to use because of choice of generator, distribution, seeding of state handling (and in turn re-entrency and thread-safety), etc.

Often, however, when creating mockup objects in unit tests we really just want a simple way of creating a random object of a specific type and don't care that must about specific parameters. I personally think C++ STL and Boost lacks an easy and reusable way of accomplishing this. We would really just want to say, for instance,

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

using some global state and only if needed be more specific like

some_nice_namespace::randomize(x, rng_state);

or even more specific like

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

Anybody that have worked both in for example Matlab and C/C++ should be very aware of this gap in productivity. Does any C++ library implement any of these ideas? If not I will implement them myself and perhaps add them to Boost.

War es hilfreich?

Lösung

Boost.Random doesn't seem to provide a class that binds a generator together with a distribution. You can make a template functor class that binds the two together.

Boost.Foreach and Boost.Range are useful for writing generic code that operates on any container, array, or pair of iterators.

I've come up with the following which allows you to randomize a container concisely, once you've defined a functor that binds a generator and a distribution.

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

By using template specialization, you can provide default generators for various numeric types:

//------------------------------------------------------------------------------
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";
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top