概要
乱数生成器をカスタム実装し、乱数分布ライブラリ 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++ コンパイラのフラグを設定する
以下の内容の Makefile を用意する。以下の CXXFLAGS の値は clang++ と g++ で共通して
利用できる。
CXXFLAGS = -std=c++20 -Wall -Wextra --pedantic-errors
乱数分布ライブラリで乱数を望みの範囲に加工する
ファイル名 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
乱数生成器をカスタム実装する
先のファイルの先頭に乱数生成器のカスタム実装クラス RandomEngine
を加える。
// Implemented by Linear Congruential generator
class RandomEngine
{
public:
using result_type = unsigned int;
private:
unsigned int a = 3;
unsigned int x;
unsigned int c = 5;
// int m;
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 UINT_MAX;
}
};
実装上のポイント
ここで型エイリアス RandomEngine::result_type
が非負で定義されていることに注意。
LLVM のサブプロジェクトlibc++ による C++ 標準ライブラリの実装では、 std::uniform_int_distribution
のオブジェクトが引き受ける乱数生成器について、非負の乱数を生成することを求めている。
詳しくは、libc++ プロジェクトの include/__random/is_valid.h で定義されている __libcpp_random_is_valid_urng
を参照のこと。
カスタム実装した乱数生成器に置き換える
#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