Hatena::ブログ(Diary)

C++でゲームプログラミング

2013-04-14

[][]Sprout で乱数配列を生成する

Sprout で乱数配列を生成する場合、sprout::generate と sprout::random::combine を使用します。


[ソース]

#include <sprout/random.hpp>
#include <sprout/array.hpp>
#include <sprout/algorithm.hpp>
#include <iostream>

int
main(){
    static constexpr sprout::default_random_engine engine;
    static constexpr sprout::uniform_smallint<int> dist(1, 6);

    static constexpr auto result = sprout::generate(
        sprout::array<int, 10>{},
        sprout::random::combine(engine, dist)
    );
    
    static_assert(
        result == sprout::make_array<int>(1, 1, 5, 2, 4, 2, 6, 2, 5, 1),
    "");

    for(auto&& n : result){
        std::cout << n << std::endl;
    }

    return 0;
}

[出力]

1
1
5
2
4
2
6
2
5
1

[参照]

http://d.hatena.ne.jp/boleros/20120928/1348820349

2013-04-13

[][]Sprout.Random で次の乱数を取得する

constexpr 時の Sprout.Random は内部の値が変更できない為、標準ライブラリ <random> とは違い operator() を何回呼び出しても同じ値しか返ってきません。

ではどうやって次の乱数値を取得するのかというと operator() を再度呼び出します。


[ソース]


#include <sprout/random.hpp>
#include <iostream>

// n 回分 () の呼び出し
template<typename Gen>
constexpr auto
call_n(Gen&& gen, int index)
->decltype(*gen()){
    return index <= 0 ? *gen() : call_n(gen(), index-1);
}


int
main(){
    static constexpr sprout::hellekalek1995 engine;
    static_assert(engine()   == 2110608584, "");
    static_assert(engine()() == 239248507, "");

    static constexpr sprout::uniform_int_distribution<int> dist1(100, 999);
    
    static_assert(dist1(engine)()       == 200, "");
    static_assert(dist1(engine)()()     == 566, "");
    static_assert(dist1(engine)()()()   == 255, "");
    static_assert(dist1(engine)()()()() == 175, "");

    static_assert(call_n(dist1(engine), 0) == 200, "");
    static_assert(call_n(dist1(engine), 1) == 566, "");
    static_assert(call_n(dist1(engine), 2) == 255, "");
    static_assert(call_n(dist1(engine), 3) == 175, "");

    return 0;
}

おもしろいですね。

(というか考えていたような呼び出し方が既に実装されていた感じ。


あと constexpr ではないけど const じゃない operator() を呼び出した場合は内部の値を変更して標準ライブラリと同じような振る舞いにすれば実行時乱数エンジンとしても使用できるんじゃないかと思った。

……振る舞いが異なるしややこしいかな?

2013-04-11

[][]Sprout.Random でコンパイル時に暗号化の鍵を生成

やってみました。


[ソース]

#include <algorithm>

struct ango{
    explicit ango(int key) : key(key){}

    template<typename Range>
    Range
    operator ()(Range range) const{
        typedef typename std::iterator_traits<decltype(std::begin(range))>::value_type value_type;
        auto result = std::move(range);
        std::transform(std::begin(result), std::end(result), std::begin(result),
            [this](value_type const& value){
            return value ^ this->key;
        });
        return result;
    }

private:
    int key;
};


#include <string>
#include <iostream>
#include <sprout/random.hpp>

int
main(){
    static constexpr int key = sprout::default_random_engine(SPROUT_UNIQUE_SEED)();
    ango filter(key);

    std::string str("satikokawaii");

    auto crypted = filter(str);
    std::cout << crypted << std::endl;

    auto decoded = filter(crypted);
    std::cout << decoded << std::endl;

    return 0;
}

[出力]

928287509
fta|~z~tbt||
satikokawaii

こんな感じで Sprout.Random でコンパイル時に鍵を設定しています。

実行時は同じ値だけどコンパイルするたびに違う鍵が生成されます。

暗号化としての良し悪しは兎も角として、アプローチとしては面白いんじゃなかろうかと。

2013-04-10

[][]Sprout.Random で乱数の種を設定

Sprout.Random で乱数の種を設定する場合、乱数エンジンのコンストラクタに整数値で種を渡します。

また、SPROUT_UNIQUE_SEED マクロを使用する事でコンパイル毎に違う値の種を設定する事が出来ます。


[ソース]

#include <sprout/random.hpp>
#include <iostream>
#include <boost/mpl/print.hpp>

int
main(){
    static constexpr sprout::uniform_int_distribution<int> dist(100, 999);

    {
        static constexpr auto seed = 2013;
        static constexpr sprout::default_random_engine engine(seed);

        static_assert(engine() == 33832491, "");
        static_assert(dist(engine) == 114, "");
    }

    {
        static constexpr auto seed = 8379842;
        static constexpr sprout::default_random_engine engine(seed);

        static_assert(engine() == 1253567439, "");
        static_assert(dist(engine) == 625, "");
    }

    {
        static constexpr auto seed = SPROUT_UNIQUE_SEED;
        std::cout << seed << std::endl;

        static constexpr sprout::default_random_engine engine(seed);
        std::cout << engine() << std::endl;
        std::cout << dist(engine) << std::endl;

        // コンパイル毎に違う値が出力される
        typedef boost::mpl::print<boost::mpl::int_<seed>>::type unique_seed_type;
        typedef boost::mpl::print<boost::mpl::int_<engine()>>::type engine_type;
        typedef boost::mpl::print<boost::mpl::int_<dist(engine)>>::type dist_type;
    }

    return 0;
}

[出力]

2600914310
1537173485
744

こんな感じでコンパイル毎に違う乱数を生成する事も出来ます。

便利。

2013-04-08

[][]Sprout.Random でコンパイル乱数

少し使ってみました。


[ソース]

#include <sprout/random.hpp>
#include <iostream>

int
main(){
    namespace random = sprout::random;

    static constexpr random::hellekalek1995 engine{};

    // 100 ~ 999
    static constexpr random::uniform_int_distribution<int> dist1(100, 999);
    static_assert(dist1(engine) == 984, "");
    // 2回目も同じ結果
    static_assert(dist1(engine) == 984, "");
    static_assert(dist1(engine) == dist1(engine), "");

    // 0.0 ~ 1.0
    static constexpr random::uniform_real_distribution<double> dist2(0.0, 1.0);
//  static_assert(dist2(engine) == 0.982829, "");
    static_assert(sprout::abs(dist2(engine) - 0.982829) < 0.001, "");

    return 0;
}

基本的には標準ライブラリの <random> と同じような感じで使用出来ます。

ただし、constexpr なので常に同じ値の乱数が返ってくるので複数の乱数値を生成する場合にはもう少し工夫して使用する必要があります。

しかし、標準ライブラリのようにジェネレータもいくつか用意されているぽいし、ぱない。


[参照]

http://d.hatena.ne.jp/boleros/20110930/1317355718