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