2

C++11 の新しい Random または Boost.Random は、非常にクールで強力で柔軟性がありますが、generatordistribution状態処理シード(および再入性とスレッド セーフ) などの選択が原因で、使用するのが面倒です。

しかし、多くの場合、単体テストでモックアップ オブジェクトを作成する場合、特定の型のランダム オブジェクトを作成する簡単な方法が必要であり、特定のパラメーターについては気にしません。個人的には、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は、ジェネレーターをディストリビューションと一緒にバインドするクラスを提供していないようです。2つをバインドするテンプレートファンクタークラスを作成できます。

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 に答える