2

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。

4

1 回答 1

1

Boost.Random似乎没有提供将生成器与分布绑定在一起的类。您可以创建一个将两者绑定在一起的模板仿函数类。

Boost.ForeachBoost.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";
}
于 2011-11-15T08:13:01.090 回答