web-dev-qa-db-ja.com

random_deviceを使用しないのはなぜですか?

私はc ++ 11ランダムライブラリについて少し混乱しています。

私が理解していること:2つの異なる概念が必要です。

  • ランダムエンジン(擬似(シードが必要)または実際のエンジン)
  • 分布:エンジンから取得した数値を、特定の分布を使用して特定の間隔にマッピングします。

私が理解していないのは、なぜこれを使用しないのかです。

std::random_device rd;
std::uniform_int_distribution<int> dist(1, 5);

// get random numbers with:
dist(rd);

私の知る限り、これはうまくいきます。

代わりに、これはほとんどの例/サイト/記事で私が見つけたものです:

std::random_device rd;
std::mt19937 e{rd()}; // or std::default_random_engine e{rd()};
std::uniform_int_distribution<int> dist{1, 5};

// get random numbers with:
dist(e);

私は特別な使用について話していません。暗号化、基本的な入門記事です。

私の疑いはstd::mt19937(またはstd::default_random_engine)はシードを受け入れます。デバッグセッション中に同じシードを指定すると、デバッグが容易になります。

また、次の理由だけではありません。

std::mt19937 e{std::random_device{}()};
24
bolov

また、次の理由だけではありません。

std::mt19937 e{std::random_device{}()};

これを1度だけ行う場合は問題ないかもしれませんが、何度も行う場合は、_std::random_device_を追跡し、不必要に作成/破棄しないことをお勧めします。

Libc ++のソースコードを調べて_std::random_device_の実装を確認すると、非常に簡単です。これはstd::fopen("/dev/urandom")の薄いラッパーです。したがって、_std::random_device_を作成するたびに、別のファイルシステムハンドルが取得され、関連するすべてのコストが支払われます。

Windowsでは、私が理解しているように、_std::random_device_はMicrosoft暗号APIへの呼び出しを表すため、これを行うたびに暗号ライブラリインターフェイスを初期化して破棄します。

それはあなたのアプリケーションに依存しますが、一般的な目的のために、私はこのオーバーヘッドを常に無視できるとは考えません。時々そうですが、これは素晴らしいことです。

これはあなたの最初の質問に結びついていると思います:

代わりに、これはほとんどの例/サイト/記事で私が見つけたものです:

_ std::random_device rd;
 std::mt19937 e{rd()}; // or std::default_random_engine e{rd()};
 std::uniform_int_distribution<int> dist{1, 5};
_

少なくとも私はそれについて考えています:

  • _std::mt19937_は非常にシンプルで信頼性の高いランダムジェネレーターです。それは自己完結型であり、OSやその他のものを呼び出さずに、完全にプロセス内に存在します。実装は標準で必須であり、少なくともブーストでは、元の_mt19937_ペーパーから派生したすべての場所で同じコードを使用しました。このコードは非常に安定しており、クロスプラットフォームです。これを初期化したり、クエリを実行したりすると、コンパイルしたプラットフォームで同様のコードにコンパイルされ、同様のパフォーマンスが得られると確信できます。

  • 対照的に_std::random_device_はかなり不透明です。あなたはそれが何であるか、それが何をするのか、それがどれほど効率的であるかを正確に知りません。実際に取得できるかどうかさえわかりません。作成しようとすると例外がスローされる可能性があります。あなたはそれが種を必要としないことを知っています。通常、そこから大量のデータを引き出すことは想定されていません。シードを生成するために使用するだけです。時には、それは暗号化APIへの素敵なインターフェースとして機能しますが、実際にそれを行う必要はなく、悲しいことに、そうでない場合もあります。 UNIXでは_/dev/random_に対応する場合があり、_/dev/urandom/_に対応する場合があります。これは、一部のMSVC暗号API(ビジュアルスタジオ)に対応する場合と、固定定数(mingw)の場合があります。あなたがいくつかの電話のためにクロスコンパイルするならば、それが何をするか知っている人。 (そして、_/dev/random_を取得した場合でも、パフォーマンスが低下する可能性があるという問題がありますconsistent-エントロピープールがなくなるまで、うまく機能しているように見える可能性があります。犬のように遅く走ります。)

私の考えでは、_std::random_device_はtime(NULL)を使用したシードの改良版のようです-time(NULL)はかなりくだらないので、それは低いバーです考えられるすべてのものをシードします。私は通常、time(NULL)を使用してシードを生成した場合に、その日に使用します。それ以外ではあまり役に立たないと思います。

26
Chris Beck

この記事 から始めるのが良いでしょう。

いくつかのポイントを合成します。

  • コストは不明です。

    この「デバイス」から数値を読み取るのはどれほどコストがかかりますか?不定です。たとえば、Linuxシステム上の/ dev/randomからの読み取りであり、エントロピーを待機するために長時間ブロックする可能性があります(それ自体がさまざまな理由で問題になります)。

私の個人的な経験のために、私はそのことを通知しましたstd::random_deviceは通常、単純な疑似ランダムアルゴリズムよりも低速です。それは一般的には真実ではないかもしれませんが、通常はそうです。これは、物理デバイス、または単純なCPU以外のハードウェアが含まれる可能性があるためです。

  • 実際には確定的である可能性があります。

    C++ 11のstd :: random_deviceは非決定的である必要はありません!実装では、固定シードを使用した単純なRNGとして実装できるため、プログラムの実行ごとに同じ出力が生成されます。

10
Biagio Festa