

MinGW版GCCでは、`std::random_device`のデフォルトコンストラクターを使うと毎回同じ数値列が生成されますが、これは規格に準拠した動作なのでしょうか？  
[実装の制限によって擬似乱数生成器を使うことは許されている](https://cpprefjp.github.io/reference/random/random_device.html)ようですが、`std::random_device`を使うということは予測不可能な乱数列を期待しているはずなので、種を固定して毎回同じ数値列が生成される（＝簡単に予測可能）のは、さすがにまずいと思うのですが。

参考：

  * [gccをwindowsで使うならstd::random_deviceを使ってはいけない - Qiita](http://qiita.com/nanashi/items/f94b78398a6c79d939e1)

[code]

    #include <random>
    #include <iostream>
    
    int main()
    {
        std::random_device rnd_gen;
        std::cout << rnd_gen() << "\n";
    }
    
[/code]

[code]

    D:\home\tmp\random>g++ --version
    g++ (tdm64-1) 5.1.0
    …
    
    D:\home\tmp\random>g++ -Wall -std=c++11 test_random_device.cpp
    
    D:\home\tmp\random>a.exe
    3499211612
    
    D:\home\tmp\random>a.exe
    3499211612
    
    D:\home\tmp\random>a.exe
    3499211612
    
[/code]


CC BY-SA 3.0
2017-08-11T12:39:41.507
37120
2017-08-15T02:15:57.357
7291
question
c++ c++11 random
std::random_deviceが生成する数値列が毎回同じなのはOK？


> 種を固定して毎回同じ数値列が生成される（＝簡単に予測可能）のは、さすがにまずいと思うのですが。

望ましい振る舞いではありませんが、ぎりぎりC++規格準拠といえる気はします。つまり "implementation-defined"
の範囲内であり、単に実装の品質が著しく低いという解釈です。（それを不具合と呼ぶかどうかはもはや基準による？）

別の観点ですが、該当環境で `std::random_device{}.entropy() == 0.0`
となっているか確認してみてください。処理系の都合で非決定的な乱数列を返せないときは、`entropy()`がゼロを返す仕様になっています。

関連する C++11仕様 §26.5.6 [rand.device] より引用（強調部分は回答者による）：

> 1 A `random_device` uniform random number generator produces non-
> deterministic random numbers.  
>  2 If implementation limitations prevent generating non-deterministic random
> numbers, **the implementation may employ a random number engine.**
>
> `explicit random_device(const string& token = implementation-defined);`  
>  3 _Effects:_ Constructs a `random_device` non-deterministic uniform random
> number generator object. **The semantics and default value of the token
> parameter are implementation-defined.**
>
> `double entropy() const noexcept;`  
>  5 _Returns:_ If the implementation employs a random number engine, returns
> 0.0. Otherwise, returns an entropy estimate for the random numbers returned
> by `operator()`, in the range `min()` to log2(`max() + 1`).

* * *

追記：「種を固定して」と表現されていますが、`random_device`コンストラクタに与えるのは乱数生成エンジンに対する種(seed)ではなく、処理系に与えるトークン(token)です。本来の意図としては、いくつかの選択肢から乱数生成エンジンを"選ぶ"ためのものです。


CC BY-SA 3.0
2017-08-12T01:17:09.697
37130
2017-08-12T01:23:25.770
2017-08-12T01:23:25.770
49
49
37120
answer


GCCの持つ`std::random_device`の主な実体ですが、

  * [gcc/libstdc++-v3/include/bits/random.h](https://github.com/gcc-mirror/gcc/blob/846deaf01bfe46e0db44402858ab1b7ee43f4023/libstdc%2B%2B-v3/include/bits/random.h#L1560-L1636)
  * [gcc/libstdc++-v3/src/c++11/random.cc](https://github.com/gcc-mirror/gcc/blob/01e91138b88a77434927fca9485e28881f52a6a0/libstdc%2B%2B-v3/src/c%2B%2B11/random.cc)

にあります。プリプロセッサマクロでコンパイル時に動作を分岐しています。

  1. [`_GLIBCXX_USE_RANDOM_TR1`](https://github.com/gcc-mirror/gcc/blob/866da453afff631a9c4a38ab18cfc1b1e719eb22/libstdc%2B%2B-v3/acinclude.m4#L2083-L2110)  
`/dev/random`か`/dev/urandom`かのどちらかのデバイスファイルが存在すれば`<random>`はTR1対応とする。ただしMinGW環境では誤検出するため常にTR1未対応とする。

  2. [`_GLIBCXX_X86_RDRAND`](https://github.com/gcc-mirror/gcc/blob/866da453afff631a9c4a38ab18cfc1b1e719eb22/libstdc%2B%2B-v3/acinclude.m4#L3966-L3984)  
x86アーキテクチャの持つ[`RDRAND`命令](https://en.wikipedia.org/wiki/RdRand)が使用可能かテストする。

の２つが影響しています。その上で実際の動作としては

  * ライブラリコンパイル時に`_GLIBCXX_USE_RANDOM_TR1`が定義され
  * アプリケーションコンパイル時に`std::random_device`コンストラクター引数に`"default"`が渡され
  * ライブラリコンパイル時に`_GLIBCXX_X86_RDRAND`が定義され
  * アプリケーション実行時にCPUが`RDRAND`命令をサポートしているかテストし

すべての条件を満たした場合には`/dev/random`も`/dev/urandom`も使用せず`RDRAND`命令を使用する実装になっていました。

個人的にな感想としては、`RDRAND`を使用する優先順位が間違っている気がします。なおアプリケーションコンパイル時に無理やり`_GLIBCXX_USE_RANDOM_TR1`を定義しても必要なライブラリ関数が用意できていないためコンパイルには成功してもリンクエラーになるものと思われます。

* * *

説明に終始していて質問に答えていませんでした。

>
> MinGW版GCCでは、std::random_deviceのデフォルトコンストラクターを使うと毎回同じ数値列が生成されますが、これは規格に準拠した動作なのでしょうか？

[bits/random.h L1568～L1587](https://github.com/gcc-
mirror/gcc/blob/846deaf01bfe46e0db44402858ab1b7ee43f4023/libstdc%2B%2B-v3/include/bits/random.h#L1568-L1587)を引用します。

[code]

    #ifdef _GLIBCXX_USE_RANDOM_TR1
    
        explicit
        random_device(const std::string& __token = "default")
        {
          _M_init(__token);
        }
    
        ~random_device()
        { _M_fini(); }
    
    #else
    
        explicit
        random_device(const std::string& __token = "mt19937")
        { _M_init_pretr1(__token); }
    
      public:
    
    #endif
    
[/code]

GCCとしては`_GLIBCXX_USE_RANDOM_TR1`が定義される環境（すなわち`/dev/random`か`/dev/urandom`かのどちらかのデバイスファイルを持ち、MinGW以外の環境）は`random_device::_M_init()`なのに対し、定義されない環境では`random_device::_M_init_pretr1()`という関数名を採用していることから読み取れるように
**Pre TR1** 、すなわちTR1以前という意識のようです。C++11どころの話ではなさそうです。


CC BY-SA 3.0
2017-08-13T00:41:48.173
37150
2017-08-15T02:15:57.357
2017-08-15T02:15:57.357
4236
4236
37120
answer