8

さて、Math.randomメソッドを使用して乱数がどのように生成されるかについて調査しています。これまでのところ、それは「ランダム」シードで始まり、そのシードはいくつかの複雑な方程式にプラグインされて乱数を作成することを学びました。シードが常に同じである場合、結果は常に同じになりますか?

Math.randomのシードは現在の時刻から生成されていると聞きましたが、それは正しいですか?ミリ秒程度まで現在の時刻を使用する必要があります。使用しなかった場合も同じ結果が得られるためです。

シードとは正確には何ですか?「10:45」のような時間ですか、それとも「10:45 11/8/12」のような時間と日付ですか、それとも何らかの組み合わせですか?

シードを見つけるにはどうすればよいので、出力を予測できますか?

これをプラグインできるようにしたい:

alert(Math.floor((Math.random()*10)+1));

私のURLバーに入れて、結果を予測できるようにします。それは可能ですか?

4

4 に答える 4

15

Rhinoのソースコードを調べて、それらが使用している疑似ランダム関数を見つけました。どうやらそれらはJava標準ライブラリで定義された関数にフォールバックします。Math.random

のドキュメントは次のようにMath.random述べています。

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

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

new java.util.Random

この新しい疑似乱数ジェネレータは、その後、このメソッドのすべての呼び出しに使用され、他の場所では使用されません。

このメソッドは適切に同期されているため、複数のスレッドで正しく使用できます。ただし、多くのスレッドが高速で疑似乱数を生成する必要がある場合は、各スレッドが独自の疑似乱数ジェネレーターを持つようにするための競合を減らすことができます。

だから私はドキュメントをチェックしてこれjava.util.Randomを見つけまし(デフォルトのコンストラクターの場合):

新しい乱数ジェネレーターを作成します。そのシードは、現在の時刻に基づく値に初期化されます。

public Random() { this(System.currentTimeMillis()); }

同じミリ秒以内に作成された2つのランダムオブジェクトは、同じ乱数シーケンスを持ちます。

これで、シードがミリ秒単位の現在の時刻であることが確実にわかりました。また、 2番目のコンストラクターのドキュメントには次のように書かれています。

単一の長いシードを使用して、新しい乱数ジェネレーターを作成します。

public Random(long seed) { setSeed(seed); }

疑似乱数ジェネレータの状態を保持するために次のメソッドによって使用されます。

メソッドのドキュメントには次のように書かれています。setSeed

単一の長いシードを使用して、この乱数ジェネレーターのシードを設定します。setSeedの一般的な契約は、この乱数ジェネレータオブジェクトの状態を変更して、引数seedをシードとして作成されたばかりの場合とまったく同じ状態になるようにすることです。setSeedメソッドは、クラスRandomによって次のように実装されます。

synchronized public void setSeed(long seed) {
    this.seed = (seed ^ 0x5DEECE66DL) & ((1L << 48) - 1);
    haveNextNextGaussian = false;
}

クラスRandomによるsetSeedの実装では、指定されたシードの48ビットのみが使用されます。ただし、一般に、オーバーライドメソッドはlong引数の64ビットすべてをシード値として使用できます。注:シード値はAtomicLongですが、haveNextNextGaussianの正しいセマンティクスを確保するには、このメソッドを同期する必要があります。

乱数を生成するために使用される実際の方法は次のnextDoubleとおりです。

この乱数ジェネレーターのシーケンスから、次の疑似乱数で均一に分散された0.0〜1.0のdouble値を返します。

関数の実装は次のnextDoubleとおりです。

public double nextDouble() {
    return (((long)next(26) << 27) + next(27))
        / (double)(1L << 53);
}

明らかにそれは機能に依存nextます:

次の疑似乱数を生成します。これは他のすべてのメソッドで使用されるため、サブクラスはこれをオーバーライドする必要があります。

関数の実装は次のnextとおりです。

synchronized protected int next(int bits) {
    seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
    return (int)(seed >>> (48 - bits));
}

それがあなたが探している疑似ランダム関数です。ドキュメントに記載されているように:

これは、DH Lehmerによって定義され、The Art of Computer Programming、Volume 2:Seminumerical Algorithms、section3.2.1でDonaldE. Knuthによって説明されている、線形合同の疑似乱数ジェネレータです。

ただし、これはRhinoで使用される乱数ジェネレーターのみであることに注意してください。SpidermonkeyやV8のような他の実装には、独自の疑似乱数ジェネレーターがある場合があります。

于 2012-11-09T06:12:31.310 に答える
6

同じミリ秒内にMath.random()を何度も呼び出すことができ、毎回異なる値を返すため、シードにはミリ秒カウントよりも多くのものがある可能性があります。

for (var i = 0; i < 3; i++) {
    console.log(Math.random(), (new Date()).getTime());
};

私の出力:

0.0617244818713516 1352433709108
0.8024995378218591 1352433709108
0.2409922298975289 1352433709108

私がそれを実装している場合、ミリ秒のカウントに基づいて最初のシードを作成し、呼び出されるたびに1を追加して、同じシード値を2回取得しないようにします。

以下からの出力を予測する100%正確な方法は次のMath.random()とおりです。

Math.random = function () { return .5; };

Math.random()は常に戻り.5ます。

于 2012-11-09T03:55:31.537 に答える
0

Date.now()シードは数値であるため、呼び出した場合(またはnew Date().getTime()古いブラウザーの場合)にシードが得られると思います。

ただし、そのシードがいつ取得されるのか、シードが現在のページに分離されているのか、ブラウザプロセス全体に共通しているのかはわかりません。乱数を予測することは非常に難しいか不可能であると思われます、それはそれらがランダムであるという全体のポイントです。

于 2012-11-09T03:52:03.433 に答える
0

いいえ、シードを予測することはできませんが、正確にブルートフォース攻撃を行うために十分な数を事前に生成することはできます。

とにかく、まずRNGのwikiページ(http://en.wikipedia.org/wiki/Random_number_generation )を読んで、PRNGの実際の実装を見てください。

于 2012-11-09T05:34:03.407 に答える