6

疑似乱数ジェネレーターを実行ごとに2回以上シードしないようにするための推奨事項をかなり多く見てきましたが、完全な説明を伴うことはありませんでした。もちろん、次の(C / C ++)の例が適切でない理由は簡単に理解できます。

int get_rand() {
  srand(time(NULL));
  return rand();
}

get_rand1秒間に数回呼び出すと、結果が繰り返されるためです。

しかし、次の例はまだ許容できる解決策ではないでしょうか?

MyRand.h

#ifndef MY_RAND_H
#define MY_RAND_H

class MyRand
{
  public:
    MyRand();
    int get_rand() const;
  private:
    static unsigned int seed_base;
};

#endif

MyRand.cpp

#include <ctime>
#include <cstdlib>
#include "MyRand.h"

unsigned int MyRand::seed_base = static_cast<unsigned int>(time(NULL));

MyRand::MyRand()
{
  srand(seed_base++);
}

int MyRand::get_rand() const
{
  return rand();
}

main.cpp

#include <iostream>
#include "MyRand.h"

int main(int argc, char *argv[]) 
{
  for (int i = 0; i < 100; i++) 
  {
    MyRand r;
    std::cout << r.get_rand() << " ";
  }
}

つまり、MyRand:sコンストラクターが連続して数回呼び出されても、への呼び出しごとsrandに異なるパラメーターがあります。明らかに、これはスレッドセーフではありませんが、やはりどちらもスレッドセーフではありませんrand

4

4 に答える 4

8

疑似乱数ジェネレーター関数を呼び出すたびに、ジェネレーターは何らかの内部状態を取得し、疑似乱数と新しい内部状態を生成します。出力がランダムに見えるように、内部状態を変換するためのアルゴリズムが慎重に選択されます。

乱数ジェネレーターをシードすると、基本的にこの内部状態が設定されます。内部状態を予測可能な値にリセットすると、ランダム性の外観が失われます。

たとえば、一般的な単純な RNG は線形合同ジェネレーターです。数値は次のように生成されます。

X[n+1] = (a X[n] + c) mod m

この場合、X[n+1] は結果と新しい内部状態の両方です。上記で提案したように毎回ジェネ​​レーターをシードすると、次のようなシーケンスが得られます。

{(ab + c) mod m, (a(b+1) + c) mod m, (a(b+2) + c) mod m, ...}

ここで、b はあなたのseed_base. これはまったくランダムに見えません。

于 2009-06-10T17:40:44.953 に答える
1

シードが予測可能な場合は、それをインクリメントしているだけなので、ここにありますが、rand() からの出力も予測可能です。

それは、乱数を生成する理由と、「乱数」が許容可能な乱数であることに大きく依存します。あなたの例では、立て続けに重複を避けることができ、それで十分かもしれません。結局のところ、重要なのはそれが実行されることです。

ほとんどすべてのプラットフォームで、rand() よりも乱数を生成するためのより良い方法があります。

于 2009-06-10T17:33:44.407 に答える
1

まあ、それは実行する必要のない余分な処理です。

そのシナリオでは、ループの開始前に時間ベースのシードを使用してコンストラクターを 1 回呼び出すだけです。これにより、反復ごとにシードを変更するという余分なオーバーヘッドなしで、ランダムな結果が保証されます。

あなたの方法はそれ以上ランダムではないと思います。

于 2009-06-10T17:35:48.287 に答える
0

乱数の生成 (これは実装に関して厳密には当てはまりませんが、例として役立ちます) を値のテーブルと考えることができます。単純なランダム サンプルを行うために統計でこのようなことを行ったことを覚えている場合、シードは基本的に、乱数の大きなテーブルで開始する行と列を示します。数がすでに正規分布していると想定できるため、何度も再シードする必要はまったくありません。

これで十分なはずなので(アプリケーションによっては)、複数回シードしてもメリットはありません。「より多くの」乱数が必要な場合は、乱数を生成する方法がたくさんあります。考えられる 1 つのケースは、スレッドセーフな方法で乱数を生成することです。

あなたのソリューションは受け入れられますが、あなたの数字は、グローバルに一度シードするよりもランダムではありません. 通常、srand はコンストラクターに属すべきではありません。乱数をサポートしたい場合は、プログラムの開始時に一度シードして、それを忘れてください。

于 2009-06-10T18:02:02.697 に答える