環境
- Windows 10
- Git Bash
$ bash --version
GNU bash, version 4.3.42(5)-release (x86_64-pc-msys)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
※コメントをいただいたので追記
今回の問題はWindowsが悪いのではなく、mingwのlibstdc++の実装が原因とのこと
(詳細はコメント欄参照)
問題
ある日以下のような手順とC++コードで、[0,2π]の一様乱数を生成しようとした
-
std::random_device
で非決定的な乱数を生成 -
mt19937
の初期シードにrandom_device
で生成した値を入れる - 実行毎に別の乱数が生成
// この乱数生成器が実行毎に別の乱数を生成するかは環境依存である
// Windows環境では実行毎に同じ値を出すので注意
// Ubuntuでは実行毎に別の乱数を生成した
std::random_device rnd;
std::mt19937 mt(rnd());
std::uniform_real_distribution<double> theta(0.0, 2*M_PI);
for(int i = 0; i < 10; i++) std::cout << theta(mt) << std::endl;
以下のようにコンパイルし
g++ kadai3.cpp -o kadai3 -std=gnu++11
実行毎に同じ乱数返すじゃん!
原因
おかしいと思い、調べてみたところ以下のページに
https://cpprefjp.github.io/reference/random/random_device.html
Windows版のGCC (MinGW, libstdc++) では、random_deviceクラスは擬似乱数生成器であるmt19937で実装されている。その環境のデフォルトでは固定の乱数列が生成されてしまうので注意すること。コンストラクタの引数としてシード値を文字列化して渡せばmt19937のシードとして扱われるが、非決定論的な乱数として振る舞わないことは変わらない。この環境ではrandom_deviceの使用は推奨しない
代替
- クロスプラットフォーム
- CPU が提供する RDRAND, RDSEED 命令
- Windows
- rand_s (CryptGenRandom のラッパー)
- RtlGenRandom 関数 (替わりに CryptGenRandom を使用することを推奨)
- CryptGenRandom 関数
と書いてありました(詳しくはリンク先を参照)
つまりWindows環境ではrandom_device
が非決定的乱数として実行されないので、上記のコードで「実行毎に別の乱数列」を生成できない模様
検証
コマンドプロンプトで実行
コマンドプロンプトのg++(バージョンは以下)でコンパイルし実行しても同じ結果になりました(実行結果は同じなので割愛)
>g++ --version
g++ (tdm64-1) 5.1.0
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
VirtualBoxのUbuntuで実行
同じWindows環境でVirtualBoxを起動しUbuntu16.04で実行したところ
$ g++ --version
g++ (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
と実行毎に別の乱数列が生成とのことで解決です
中々目的のページが見つからなくて時間かかったので、誰かのお役に立てればうれしいです