web-dev-qa-db-ja.com

std :: random_deviceからシードされたランダムエンジンを使用するか、毎回std :: random_deviceを使用する必要がありますか

ランダム性の2つのソースを含むクラスがあります。

_std::random_device rd;
std::mt19937 random_engine;
_

_std::mt19937_への呼び出しで_std::random_device_をシードします。数値を生成したいが再現性を気にしない場合は、rd()またはrandom_engine()を呼び出す必要がありますか?

私の特定のケースでは、パフォーマンスがそれほど重要ではなく、結果が特に敏感ではない一部のネットワークコードで呼び出されるため、両方とも問題なく機能すると確信しています。ただし、ハードウェアエントロピーをいつ使用するか、および疑似乱数をいつ使用するかについてのいくつかの「経験則」に興味があります。

現在、私は_std::random_device_エンジンのシードに_std::mt19937_のみを使用しており、プログラムに必要な乱数生成には、_std::mt19937_エンジンを使用しています。

編集:これは私がこの特定の例を使用している正確な説明です:

これはゲームプレイプログラム用です。この特定のゲームでは、ユーザーは対戦相手とのラウンドを開始する前に「チーム」をカスタマイズできます。戦闘の設定の一部には、チームをサーバーに送ることが含まれます。私のプログラムにはいくつかのチームがあり、乱数を使用してロードするチームを決定します。新しい戦闘ごとに_std::random_device_が呼び出され、疑似乱数ジェネレーターがシードされます。ランダムに選択したこのチームと初期シードを含む、戦闘の初期状態をログに記録します。

この質問で私が尋ねている特定の乱数は、ランダムなチーム選択のためのものです(私が使用しているチームを事前に相手に知らないことが有益ですが、ミッションクリティカルではありません)が、私は何ですか? m本当に探しているのは経験則です。数値の再現性が必要ない場合は、常に_std::random_device_を使用しても問題ありませんか、それとも、収集できるよりも早くエントロピーを使い果たすリスクがありますか?

22
David Stone

私の知る限り、標準的な方法は、コンピューターによって計算されない(ただし、外部の予測できないソースからの)数を乱数ジェネレーターにシードすることです。これは、rd()関数の場合に当てはまります。それ以降、必要なすべての疑似乱数に対して疑似乱数ジェネレーター(PRNG)を呼び出します。

数値が十分にランダムでないことが心配な場合は、別のPRNGを選択する必要があります。エントロピーは希少で貴重な資源であり、そのように扱われるべきです。現在はそれほど多くの乱数を必要としないかもしれませんが、将来的には必要になるかもしれません。または他のアプリケーションがそれらを必要とする可能性があります。アプリケーションがエントロピーを要求するたびに、そのエントロピーを利用できるようにする必要があります。

あなたのアプリケーションにとって、メルセンヌツイスターはあなたのニーズにうまく合うように思えます。あなたのゲームをプレイする人は誰も、ロードされているチームがランダムではないと感じることはありません。

10
John Sallay

暗号化に使用しない場合は、random_engineによってシードされたmt19937を繰り返し使用することをお勧めします。

この回答の残りの部分では、ネットワークコードの暗号化に乱数を使用していると仮定します。 要するに、mt19937はその用途には適していません。

http://en.wikipedia.org/wiki/Mersenne_twister#Disadvantages

攻撃者が乱数の予測を開始する可能性があるため、時間の経過とともに(おそらく間接的に)情報が漏洩する潜在的なリスクがあります。少なくとも理論的には、これがその目的です。ウィキペディアから

...この図はからの状態ベクトルのサイズであるため
どの将来の反復が生成されるか)により、将来のすべての反復を予測できます。

乱数生成情報がユーザーに漏洩するのを防ぐ簡単な方法は、一方向ハッシュ関数を使用することですが、それだけではありません。その目的のために設計された乱数ジェネレーターを使用する必要があります。

http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator

さまざまな例(コード付き)がここにあります http://xlinux.nist.gov/dads/HTML/pseudorandomNumberGen.html

8
Johan Lundberg

シミュレーションやゲームにランダム性が必要な場合は、それで問題ありません。ランダムデバイスを1回だけ呼び出し、その他はランダムにシードされた疑似RNGを使用して実行します。ボーナスとして、後で疑似ランダムシーケンスを再生できるように、シード値をログファイルに保存する必要があります。

auto const seed = std::random_device()();
// save "seed" to log file
std::mt19937 random_engine(seed);

(複数のスレッドの場合、メインスレッドでPRNGを使用して、生成されたスレッドでさらにPRNGのシードを生成します。)

暗号化の目的で多くの真のランダム性が必要な場合、a PRNGは決して良い考えではありません。ただし、出力の長いシーケンスには真のランダム性よりもはるかに少ないランダム性が含まれるため、次のことができます。小さなサブセットからすべてを予測します。真のランダム性が必要な場合は、予測できないソース(熱センサー、ユーザーのキーボード/マウスアクティビティなど)から収集する必要があります。Unixの/dev/randomはそのような「真のランダム性」のソースかもしれませんが、すぐにはいっぱいにならないかもしれません。

7
Kerrek SB

http://channel9.msdn.com/Events/GoingNative/2013/Rand-Considered-Harmful を参照して、uniform_int_distributionを使用する理由とrandom_deviceの相対的な強みを説明することをお勧めします。/mt19937。

このビデオでは、Stephan T. Lavavejが、Visual C++ではrandom_deviceを暗号化の目的で使用できると具体的に述べています。

3
Gathar

答えはプラットフォームに依存します。 Visual C++ 2010では、std :: random_deviceが文書化されていない方法でmt19937にシードされていることを覚えているようです。

もちろん、乱数ジェネレーターに基づくアドホック暗号化スキームは非常に弱い可能性が高いことに気づきます。

2
Jive Dadson