1

非常に短い時間で大量の乱数を生成する必要がある Java メソッドがあります。私の最初のアプローチは、Math.random (これは非常に高速に動作します) を使用することでしたがMath.random、他の背後で非常に迅速に呼び出すため、「ランダム」は実際にはランダムではない (またはランダムではない) と推測されます。 (ただし、できるだけランダムにする必要があります)。

2 つの質問があります。

  1. 非常に短い時間での呼び出しの数のために、ランダム出力のランダム性が低下するという私の推測は正しいですか? 1. の答えが「はい」の場合:
  2. ランダム性の少ない問題を解決するための最速の方法 (呼び出しごと) は何でしょうか?

私はすでに をSecureRandomいじりましたが、通常の Math.random よりも少なくとも 15 倍遅く、私の要件には遅すぎます。

4

7 に答える 7

4

TL;DR: あなたの推測は間違っています。

Math.random上の単一のインスタンスに作用しますjava.util.Random:

0.0 以上 1.0 未満の正符号付きの double 値を返します。返される値は、その範囲から (ほぼ) 一様に分布する疑似ランダムに選択されます。

このメソッドが最初に呼び出されると、式による場合とまったく同じように、単一の新しい疑似乱数ジェネレータが作成されます。

new java.util.Random()

JavaDocから

現在、「このコンストラクターの他の呼び出しとは異なる可能性が非常に高い」数値でシードされた線形合同式java.util.Randomを使用します。 1

これは疑似乱数進行であるため、つまり、同じシードからまったく同じ値が得られるため、数値を抽出する速度はMath.randomランダム性に影響しません。

于 2015-09-22T19:28:53.847 に答える
3

Random クラスを使用する乱数は、int をビット マングルして新しい int を生成するアルゴリズムを使用します。呼び出しの速さや回数に関係なく、同じアルゴリズムが使用されます。進行は進行です。

これをテストするには、42 などの数値をシードします。その後、進行状況を監視します。同じ番号でもう一度シードします。まったく同じ進行。

このアプローチの欠点は、数値が完全にランダムではないことです。それらはかなりランダムで、ほとんどの場合には十分ですが、完全にランダムではありません。

Random メソッドの出力を、非常に厳しい一連のテストで実行しました。1 つは境界線で、もう 1 つはまったく失敗しました。それが、私たちが話しているランダムの一種です。

さらに、日時スタンプを使用して自身をシードするため、状況によってはある程度予測可能です。毎週月曜日の朝一番にその週のタスクを起動して実行する人を想像してみてください。月曜日の朝の 8 時から 8 時 30 分までのタイムスタンプで実行されるため、ある程度の予測可能性があります。

そのため、セキュリティに関係のないほとんどの操作には Random で十分です。それらの多くでさえ。

一方、SecureRandom は真の乱数を生成します。これは、システムのタイミングや、無数の要因に基づいて秒単位で変化するその他の事柄を調べることによって行われます。

欠点は、これらの要因は 1 秒間に頻繁にしか変化しないため、SecureRandom は一定期間内に有限数の乱数しか生成できないことです。事前に生成して使用するためにキャッシュしようとしますが、キャッシュを吹き飛ばすことができます。

このように、私の逆浸透浄水器のようなものです。すでにろ過された1ガロンの水を保持します。1 ガロンの水を 1 回のショットで使用すると、フィルターでろ過された速度で水が得られます。たとえば、5 秒あたり 1 オンスなどです。最初のガロンは速く、その後は非常に遅くなります。

于 2015-09-22T19:29:05.977 に答える
0
  1. (疑似) 乱数ジェネレーターは、呼び出される頻度に関係なく、初期シード値が同じであれば同じ結果を生成します。これは完全に決定論的であり、速度には依存しません。シードの選択は (明示的に指定されていない場合) 時間に依存しますが、生成されるシーケンスには依存しません。
  2. より速い速度が必要な場合は、必要な長さよりも長い疑似乱数シーケンスの値を事前に計算してから、ジェネレーターを 1 回呼び出すだけで、シーケンス内の開始位置を選択できます。このようにして、最初の 1 回の呼び出しの後、後続のすべての実行で値を簡単に読み取ることができます。パフォーマンスは、テーブルを保持しているメモリのインデックス作成と読み取りの速度によって制限されます。アプリケーションによっては、シーケンスの潜在的な再利用が推奨されない場合があります。
于 2015-09-22T19:41:03.023 に答える
0

Java KissライブラリAESPRNG内部ジェネレーターを試してください。スレッド セーフであり、一括リクエストで使用すると Random の約 2 倍の速さで、128 ビットの暗号的に強力な (ただし、シードをリセットすると再現可能) 疑似乱数を生成できます。これは、ほとんどのシステムで高度に最適化された AES CTR モードに基づいています。

kiss.util.AESPRNG prng = new kiss.util.AESPRNG();
double [] x = new double [1_000_000];
prng.nextDoubles(x,0,x.length);

繰り返し可能なシーケンスが必要な場合は、seed(byte[] value16) または seed(double value) を使用します。シーケンスをリセットします。これは Random のドロップイン置換ですが、範囲またはバルク番号のための便利なメソッドが多数あります。これは、提案されている代替手段のどれよりも優れています。高速で、繰り返し可能で、128 ビットの強力なランダムです。

于 2016-08-13T06:56:43.583 に答える