2

Boost の正規分布を使用して、異なるシードを指定して乱数を生成しようとしています。つまり、seed1、seed2 などに対して生成された同じ乱数が必要です。シミュレーション中に数千のシードが関数に渡されます。乱数ジェネレーターがシードなしで使用されることはありません。[編集: 「キー」は「シード」よりも適切な言葉です。以下の最終的な説明ブロックを参照してください。]単一の RNG を生成して再シードすることが最も理にかなっているのか (もしそうなら、どのように) または毎回新しいものを生成する方が簡単な場合。ここに私がこれまでに持っているものがあります。これには、ランダムな正規数のリクエストごとに新しいシードされた rng の構築が含まれます。


double rnorm( int thisSeed ) {
  boost::mt19937 rng( thisSeed );
  boost::normal_distribution<> nd( 0.0, 1.0 ); // (mean, sd)
  boost::variate_generator > var_nor( rng, nd );
  return var_nor();
}

これはばかげていますか?PRNG、特に Boost の実装は初めてです。


私がこれを行っている理由のより完全な説明:

タンパク質相互作用をシミュレートするために巨大なランダム エネルギー ランドスケープを作成しています。各配列には、特定の位置の特定のアミノ酸の値 (および他のいくつかの配列属性) に依存する消光ガウス乱数の合計として計算される特定のエネルギーがあります。PRNG を使用して、これらの疑似乱数の値を計算したいと思います。これらの値は一貫している必要があります (同じシーケンスは同じ値を生成する必要があります) が、格納するには多すぎます。簡単な例として、シーケンス ARNDAMR があり、2 つのサブエネルギーに基づいてその総エネルギーを計算するとします。最後の 3 つのアミノ酸に依存します。私' PRNG のシード (引数) として使用するために、構成をキーに変換します。何千ものシーケンスが構築されて変異するため、エネルギーをすばやく計算する方法が必要です。そのため、RNG をシードして呼び出す最善の方法を知る必要があります。これらのエネルギー値の「ルックアップ」以外の目的でブースト RNG を使用することはありません。


さらに(tl;dr)説明:

1 から 10^6 または 10^7 までの整数である「キー」値を使用します。それぞれをガウス乱数にマップしたい。キー値とその数値の間に相互相関があってはなりません (たとえば、キー 145 ~ 148 は自己相関の「乱数」にマップされるべきではありません)。

シミュレーションでキー (キー) が呼び出されるたびに同じ乱数を返すには、特定のキーが必要です。キーと乱数のペアをルックアップ テーブルに格納したくありません。

4

1 に答える 1

2

あなたのアプローチは、PRNGの仕組みを根本的に誤解しています。使用するたびに再シードすると、乱数はまったく得られず、シードの悪いハッシュ関数が得られます。特に、PRNG の正規分布関数を呼び出しても、数値は正規分布しません。これは、PRNGは特定のシードから生成された乱数が正規分布であることのみを保証するためです。

特定の入力セットに対して繰り返し可能な大量の乱数セットが必要な場合は、それらの入力の関数である単一の数値を生成し、それを PRNG にシードしてから、PRNG から予測可能なシーケンスで数値を取得します。同じ入力に対して同じシーケンスが生成され、PRNG によって数値が適切に分配されます。

ランダム シーケンスを決定するために使用する入力のセットが大きい場合 (特に、PRNG のシードのサイズよりも大きい場合)、入力のすべてのセットに固有のシーケンスはありません。それはあなたのアプリケーションにとっては問題ないかもしれませんし、より大きなシードを持つ PRNG を使用したいかもしれません。

私のパブリック ドメインojrandlibを見てください。大きなシードを使用し、高速のジグラット アルゴリズムで正規分布の数値を生成します。


あなたの明確化を見た後に編集してください:

あ、なるほど。「a」ガウスランダムなどというものはありません。配布は、1 つのシードからのシーケンス全体に関してのみ意味があるため、必要なことは、単一のジェネレーターを作成してシードし、そのジェネレーターからキー N ごとに N 番目のランダム値をフェッチすることです。これを順番に (つまり、シーケンスの一部としてではなく、完全にランダムにキーからフェッチしている場合)、これは非常に遅くなりますが、それでも可能です。キーを取得する前にキーをソートするなどして、シーケンスを強制できるかどうかを確認したい場合があります。

ojrandlib にはdiscard()このための機能もあるため、シーケンスの 1,000,000 番目の数を見つける必要がある場合は、PRNG をシードして 999,999 個を破棄できます。これは、実際にそれらを生成するよりも高速ですが、それでもかなり遅くなります。

おそらくより良い: キーを使用してガウス ジェネレーターをシードする代わりに、キー + 固定シードの適切なハッシュ関数を計算し (これにより、ランダムなビットが均一に分散されます)、それらのハッシュ ビットを 2 つの均一なフロートとして解釈し、Box を実行します。 -ミュラーまたはジッグラトを使用して分布を変換します。そうすれば、取得する数値はすべて同じ「シード」(ハッシュへの入力) からのものになりますが、正規分布します。暗号的に安全なハッシュは必要ないので、MurMurHash のようなものがうまくいくかもしれませんが、そのような特別な目的のために独自に作成したほうがよいでしょう。


私のライブラリのユーザーがあなたと同様の問題を抱えているのではないかと考えたので、いくつかの可能性を調査しました。あなたのために働くかもしれないいくつかのコードはここにあります:

/* Thomas Wang's 32-bit integer hash */
uint32_t nth_rand32(uint32_t a) {
    a -= a << 6;
    a ^= a >> 17;
    a -= a << 9;
    a ^= a << 4;
    a -= a << 3;
    a ^= a << 10;
    a ^= a >> 15;
    return a;
}

/* Marsaglia polar method */
double nth_normal(int index) {
    double f, g, w;
    int skip = 0;
    uint64_t x, y;

    do {
        x = (uint64_t)nth_rand32((index & ~1) + skip);
        y = (uint64_t)nth_rand32((index | 1) + skip);
        skip += 0x40000001;

        x = (x << 20) | 0x3ff0000000000000ull;
        f = *(double *)(&x) * 2.0 - 3.0;
        y = (y << 20) | 0x3ff0000000000000ull;
        g = *(double *)(&y) * 2.0 - 3.0;

        w = f * f + g * g;
    } while (w >= 1.0 || w == 0.0);

    w = sqrt((-2.0 * log(w)) / w);

    if (index & 1) w *= f;
    else w *= g;
    return w;
}

ハッシュは絶対に合格しませんが、かなり良いです。10,000,000 のランダムな法線を生成し、この分布を得ました (この画像のアップロードが機能する場合):

分布

完璧ではありませんが、それほど悪くはありません。より高価なハッシュを使用する方がはるかに優れていますが、速度と精度のトレードオフがどこにあるかはあなたに決めてもらいます。

于 2013-06-28T00:40:22.170 に答える