ボレロ村上 - ENiyGmaA Code

中3女子です。

Sprout.Random - コンパイル時の乱数生成

Sprout.Random - コンパイル時の乱数生成


実装してみた。
Github https://github.com/bolero-MURAKAMI/Sprout


ヘッダは です。
シードとして使える SPROUT_UNIQUE_SEED マクロは にあります。


現時点で実装済みであるのは、
 ・ジェネレータ minstd_rand0, minstd_rand
 ・分布 uniform_smallint, uniform_01
です。
これから対応を増やしていきたいと思います。


基本的に Boost.Random や標準ライブラリの のインタフェースを踏襲しています。

大きく違う部分は、乱数を生成するジェネレータや分布クラス operator()() が常に const であることと、
結果型が「生成された乱数値」と「次の状態の乱数生成器」が格納されたクラスであることです。

constexpr では関数型言語のように状態の変更が出来ないため、このような仕様になっています。


以下、テストコードと出力結果。

    • テストコード
#include <cstddef>
#include <iostream>
#include <algorithm>
#include <iterator>
#include <type_traits>
#include <sprout/array.hpp>
#include <sprout/random.hpp>
#include <sprout/random/unique_seed.hpp>

//
// random_array
//
//  乱数生成器 Gen を使って乱数値の配列を生成する。
//
template<typename T, std::size_t N, typename Gen>
constexpr sprout::array<T, N> random_array(Gen const& gen);

//
// random_array_impl
//
//  要素数が指定の数に達した場合、配列を生成して返す。
//
template<typename T, std::size_t N, typename Random, typename... Args>
constexpr typename std::enable_if<
    sizeof...(Args) == N - 1,
    sprout::array<T, N>
>::type random_array_impl(Random const& rnd, Args const&... args) {
    return sprout::array<T, N>{{args..., *rnd}};
}
//
//  要素数が指定未満の場合、乱数値を生成して再帰。
//  Random は、乱数生成器が返す結果型。「生成された乱数値」と「次の状態の乱数生成器」が格納されている。
//  constexpr では関数型言語のように状態の変更が出来ないため、このような仕様になっている。
//
template<typename T, std::size_t N, typename Random, typename... Args>
constexpr typename std::enable_if<
    sizeof...(Args) < N - 1,
    sprout::array<T, N>
>::type random_array_impl(Random const& rnd, Args const&... args) {
    //
    //  operator*() で、「生成された乱数値」を取得。
    //  operator()() で、「次の状態の乱数生成器」を呼び出す。
    //
    return random_array_impl<T, N>(rnd(), args..., *rnd);
}

//
// random_array の実装
//
//  乱数生成器の最初の呼出。
//
template<typename T, std::size_t N, typename Gen>
constexpr sprout::array<T, N> random_array(Gen const& gen) {
    return random_array_impl<T, N>(gen());
}

int main() {
    //
    //  SPROUT_UNIQUE_SEED は constexpr として使える、ほぼユニークかつコンパイル毎に変化する std::size_t 値になる。
    //  実装は、日時とファイル名と行の文字列からハッシュ値を生成している。
    //
    static constexpr std::size_t seed = SPROUT_UNIQUE_SEED;
    //
    //  minstd_rand0 は 線形合同法エンジンのジェネレータ。
    //
    static constexpr auto engine = sprout::random::minstd_rand0(seed);
    //
    //  uniform_smallint は 整数の一様分布。
    //  ここでは [1 .. 6] の範囲。
    //
    static constexpr auto distribution = sprout::random::uniform_smallint<int>(1, 6);
    //
    //  random_array を使って乱数値の配列を生成。
    //  combine は、ジェネレータと分布を一つの乱数生成器(variate_generator)にまとめる。
    //
    static constexpr auto arr = random_array<int, 10>(sprout::random::combine(engine, distribution));

    //
    //  配列を標準出力に出力。
    //
    std::copy(arr.begin(), arr.end(), std::ostream_iterator<int>(std::cout, ", "));
    std::cout << std::endl;
}
    • 実行結果
5, 2, 2, 6, 1, 4, 4, 4, 3, 6,