21

C ++ rand()関数に基づいて、プログラムで数千のオブジェクトを生成します。それらをメモリに保持することは徹底的です。rand()の現在のシードをいつでもコピーする方法はありますか?これにより、完全なオブジェクトではなく、現在のシードのみを保存する機会が得られます。(したがって、乱数のまったく同じサブシーケンスを再生成することで、これらのオブジェクトを再生成できます)

徹底的な解決策は、 rand()によって与えられた乱数の完全なシーケンスを格納することです-それは価値がありません。もう1つの解決策、ランダム化された数値に対して独自のクラスを実装することです。

グーグルは私に前向きな手がかりを与えなかった。randとsrandの基本を教えている記事は何百もありますが、具体的な記事は見つかりませんでした。

シードスティーラーが実装された他の乱数ジェネレーターを知っている人はいますか?


迅速な回答ありがとうございます!この質問に対する可能な回答/解決策は他にもあるので、ここにあなたの回答のリストを作成しました。

ソリューション:

  1. 簡単な答えは次のとおりです。シードを取得する標準的な方法はありません

  2. 最も近い回避策は、最初にINITIALシードを保存し、rand()関数を呼び出した回数を数えることです。これは、すべてのコンパイラの現在のstd :: rand()関数で機能するため、これを解決策としてマークしました(これが主な質問でした)。2.0 GHz CPUのベンチマークを行ったところ、35秒間にrand()を1,000,000,000回呼び出してカウントできることがわかりました。これは良さそうに聞こえるかもしれませんが、1つのオブジェクトを生成するために80,000回の呼び出しがあります。unsignedのサイズが長いため、これにより世代数が50,000に制限されます。とにかく、ここに私のコードがあります:

    class rand2
    {
       unsigned long n;
    
       public:
    
       rand2 () : n(0) {}
    
       unsigned long rnd()
       {
          n++;
          return rand();
       }
       // get number of rand() calls inside this object
       unsigned long getno ()
       {
          return n;
       }
       // fast forward to a saved position called rec
       void fast_forward (unsigned long rec)
       {
          while (n < rec) rnd();
       }
    };
    
  3. もう1つの方法は、Matteo Italiaが提案したような、独自の疑似乱数ジェネレーターを実装することです。これは最速で、おそらく最良のソリューションです。4,294,967,295 rand()呼び出しに制限されておらず、他のライブラリも使用する必要はありません。コンパイラが異なればジェネレータも異なることは言及する価値があります。MatteoのLCGをMingw/GCC3.4.2およびG++4.3.2のrand()と比較しました。それらの3つすべてが異なっていました(シード= 0)。

  4. Cubbi、Jerry Coffin、Mike Seymourが提案したように、C++11または他のライブラリのジェネレーターを使用します。すでにそれらを使用している場合は、これが最良のアイデアです。C ++ 11ジェネレーターのリンク:http://en.cppreference.com/w/cpp/numeric/random (ここにもいくつかのアルゴリズムの説明があります)

4

8 に答える 8

13

シードスティーラーが実装された他の乱数ジェネレーターを知っている人はいますか

すべての標準C++11乱数ジェネレーター(TR1およびBoostでも使用可能)は、この機能を提供します。ジェネレータオブジェクトをコピーするか、シリアル化/逆シリアル化するだけです。

于 2012-04-17T20:43:40.467 に答える
8

現在のシードを取得する標準的な方法はありませんが(を介してのみ設定できます)、数行のコードで自分でsrand再実装できますrand()(通常は線形合同法です)。

class LCG
{
private:
    unsigned long next = 1;
public:

    LCG(unsigned long seed) : next(seed) {}

    const unsigned long rand_max = 32767

    int rand()
    {
        next = next * 1103515245 + 12345;
        return (unsigned int)(next/65536) % 32768;
    }

    void reseed(unsigned long seed)
    {
        next = seed;
    }

    unsigned long getseed()
    {
        return next;
    }
};
于 2012-04-17T20:43:34.390 に答える
6

srand()を使用してシードを設定します。シードとして使用した値を保存します。

http://cplusplus.com/reference/clibrary/cstdlib/srand/

于 2012-04-17T20:42:52.370 に答える
5

C ++ 11の乱数生成クラスはoperator<<、状態(主にシード)を格納しoperator>>て読み戻すことをサポートしています。したがって、基本的に、オブジェクトを作成する前に状態を保存し、次に同じものを再生成する必要がある場合シーケンスを実行し、状態を読み返して、オフにします。

于 2012-04-17T20:43:51.857 に答える
4

rand()シードを抽出または複製する方法は提供されていません。最善の方法は、シードを設定するときにシードの初期値を保存し、srand()そこからシーケンス全体を再構築することです。

Posix関数rand_r()を使用すると、シードを制御できます。

C ++ 11ライブラリには、シーケンス生成「エンジン」に基づく乱数ライブラリが含まれています。<<これらのエンジンはコピー可能であり、演算子を使用して状態を抽出および復元>>できるため、シーケンスの状態をいつでもキャプチャできます。C ++ 11をまだ使用できない場合は、TR1とBoostで非常によく似たライブラリを使用できます。

于 2012-04-17T20:46:20.340 に答える
1

rand()の現在のシードをいつでもコピーする方法はありますか?

以下は、Ubuntu Linux(14.04および16.04でテスト済み)のCライブラリで動作する疑似乱数ジェネレーター(PRNG)の状態を保存および復元するための実装固有の方法です。

#include <array>
#include <cstdlib>
#include <iostream>

using namespace std;

constexpr size_t StateSize = 128;
using RandState = array<char, StateSize>;

void save(RandState& state) {
    RandState tmpState;
    char* oldState = initstate(1, tmpState.data(), StateSize);
    copy(oldState, oldState + StateSize, state.data());
    setstate(oldState);
}

void restore(RandState& state) {
    setstate(state.data());
}

int main() {
    cout << "srand(1)\n";

    srand(1);

    cout << "  rand(): " << rand() << '\n';
    cout << "  rand(): " << rand() << '\n';
    cout << "  rand(): " << rand() << '\n';
    cout << "  rand(): " << rand() << '\n';
    cout << "  rand(): " << rand() << '\n';
    cout << "  rand(): " << rand() << '\n';
    cout << "  rand(): " << rand() << '\n';
    cout << "  rand(): " << rand() << '\n';

    cout << "srand(1)\n";

    srand(1);

    cout << "  rand(): " << rand() << '\n';
    cout << "  rand(): " << rand() << '\n';
    cout << "  rand(): " << rand() << '\n';
    cout << "  rand(): " << rand() << '\n';

    cout << "save()\n";

    RandState state;
    save(state);

    cout << "  rand(): " << rand() << '\n';
    cout << "  rand(): " << rand() << '\n';
    cout << "  rand(): " << rand() << '\n';
    cout << "  rand(): " << rand() << '\n';

    cout << "restore()\n";

    restore(state);

    cout << "  rand(): " << rand() << '\n';
    cout << "  rand(): " << rand() << '\n';
    cout << "  rand(): " << rand() << '\n';
    cout << "  rand(): " << rand() << '\n';
}

これは以下に依存します:

  1. rand()random()インターフェイスの両方を公開するためにCライブラリで使用されているのと同じPRNG 、および
  2. Cライブラリ(128バイト状態)でのこのPRNGのデフォルトの初期化に関する知識。

実行すると、次のように出力されます。

srand(1)
  rand(): 1804289383
  rand(): 846930886
  rand(): 1681692777
  rand(): 1714636915
  rand(): 1957747793
  rand(): 424238335
  rand(): 719885386
  rand(): 1649760492
srand(1)
  rand(): 1804289383
  rand(): 846930886
  rand(): 1681692777
  rand(): 1714636915
save()
  rand(): 1957747793
  rand(): 424238335
  rand(): 719885386
  rand(): 1649760492
restore()
  rand(): 1957747793
  rand(): 424238335
  rand(): 719885386
  rand(): 1649760492

このソリューションは場合によっては役立ちますが(変更できないコード、デバッグ目的で実行を再現するなど)、一般的なソリューションとしては明らかにお勧めできません(たとえば、これを適切にサポートするC ++ 11PRNGを使用するなど)。 )。

于 2016-09-15T22:50:33.920 に答える
0

srandの直前(または直後)にシードするために使用した値を保存してみることができます。

したがって、たとえば:

int seed = time(NULL);
srand(time(NULL));

cout << seed << endl;
cout << time(NULL);

2つの値は同じである必要があります。

于 2012-04-17T20:56:41.863 に答える
0

メルセンヌツイスター疑似乱数ジェネレーターの使用をお勧めします。高速で、非常に優れた乱数を提供します。非常に簡単に、クラスのコンストラクターにジェネレーターをシードできます。

unsigned long rSeed = 10;
MTRand myRandGen(rSeed);

次に、シーケンスの生成に使用したシードをどこかに保存する必要があります...

于 2014-02-21T04:17:44.430 に答える