2

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.

Nordlöw
  • 11,838
  • 10
  • 52
  • 99
  • The opening premise doesn't make sense. How does a `` gaussian distribution built from an engine even *compare* to `rand()`? They do completely different things. It's like saying that driving a car isn't as easy as eating cake, but eating cake won't get you to soccer practice... – Kerrek SB Nov 14 '11 at 14:20
  • 1
    As a side note, tests should not use random data, because then they are not guaranteed to be reproducable. – Björn Pollex Nov 14 '11 at 14:21
  • Ok, I removed the comparison with `rand()`. I still think my ideas are motivated. – Nordlöw Nov 14 '11 at 14:23
  • @BjörnPollex: They are reproducible if the random number generator always starts with the same seed. – Emile Cormier Nov 15 '11 at 08:19

1 Answers1

1

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";
}
Emile Cormier
  • 28,391
  • 15
  • 94
  • 122