概要
乱数生成器をカスタム実装し、乱数分布ライブラリ std::uniform_int_distribution
と組み合わせて使用する方法を実践的に説明する。
ソースコードについては、https://github.com/youpong/RandomEngine を利用できる。
必要条件
プログラムは、標準規格 C++20 に準拠している。そのため、新しいバージョンの C++ コンパイラーと C++ 標準ライブラリの実装が必要になる。
テスト環境
以下の環境でテストしている。
- macOS 15.2
- Xcode 16.2 (バンドルされる Apple Clang 16.0.0 を使用)
- Ubuntu 24.04.1 LTS
- GCC 13.3.0
- Clang 18.1.3
手順
ここでは、次のような流れで開発する。
- C++標準ライブラリの乱数生成器 std::default_ramdom_engine と乱数分布 std::uniform_int_distribution を組み合わせたプログラムを作成する。
- 乱数生成器 RandomEngine をカスタム実装する。
- 乱数生成器をカスタム実装の RandomEngine と置き換える。
C++ コンパイラのフラグを設定する
以下の内容の Makefile を用意する。プログラムが C++20 規格に準拠していることと、
コンパイル時のワーニングをなるべく多くだすようにしている。
CXXFLAGS = -std=c++20 -Wall -Wextra --pedantic-errors
C++標準ライブラリの乱数生成器と乱数分布を組み合わせる
ファイル名 main.cpp で以下の内容を保存する。
#include <iostream>
#include <random>
int main() {
std::uniform_int_distribution<int> d(1, 6);
std::default_random_engine e;
for (auto i = 0; i != 10; ++i) {
std::cout << d(e) << " ";
}
}
実行する
$ make main && make run
実行結果は以下の様になる。
実行結果は環境によって異なるだろう。
2 6 5 1 3 1 1 3 3 3
乱数生成器をカスタム実装する
乱数生成クラスは、以下のクラスメソッドを実装する必要がある。
- min() - 生成する乱数の最小値を返す
- max() - 生成する乱数の最大値を返す
以下のインスタンスメソッドを実装する必要がある。
- operator() - 乱数を生成する
以下の型エイリアスを実装する必要がある
- result_type - 生成する乱数の型(非負1)
具体的な実装例は以下の様になる。
class RandomEngine {
public:
using result_type = unsigned int;
private:
// params from cc65 compiler
unsigned int a = 16'843'009;
unsigned int x;
unsigned int c = 826'366'247;
public:
RandomEngine(unsigned int seed = 0) : x(seed) {}
result_type operator()() {
x = a * x + c;
return x;
}
static constexpr result_type min() { return 0; }
static constexpr result_type max() {
return std::numeric_limits<unsigned int>::max();
}
};
カスタム実装した乱数生成器に置き換える
#include <iostream>
#include <random>
int main() {
std::uniform_int_distribution<int> d(1, 6);
RandomEngine e; // <- 置き換えた
for (auto i = 0; i != 10; ++i) {
std::cout << d(e) << " ";
}
}
実行する
$ make main && make run
実行結果は以下の様になる。
6 5 2 1 6 5 2 1 6 5
以上です。
参考文献
- 江添亮. 江添亮のC++入門. ドワンゴ, 2019, Kindle版, 39.3 乱数分布.
-
LLVM のサブプロジェクトlibc++ による C++ 標準ライブラリの実装では、
std::uniform_int_distribution
のオブジェクトが引き受ける乱数生成器について、非負の乱数を生成することを求めている。
詳しくは、libc++ プロジェクトの include/__random/is_valid.h で定義されている__libcpp_random_is_valid_urng
を参照のこと。 ↩