21

我基于 C++ rand()函数在我的程序中生成了几千个对象。将它们保存在内存中将是详尽无遗的。有没有办法在任何给定时间复制 rand() 的当前种子?这将使我有机会仅存储当前种子而不是完整对象。(因此我可以通过重新生成完全相同的随机数子序列来重新生成这些对象)

一个详尽的解决方案是存储由rand()给出的完整随机数序列- 不值得。另一个解决方案是实现我自己的随机数类。

谷歌没有给我任何积极的线索。有数百篇文章教授 rand 和 srand 的基础知识,我找不到具体的。

有谁知道其他带有种子窃取器的随机数生成器?


感谢您的快速答复!这个问题有更多可能的答案/解决方案,所以我在这里列出了你的答案。

解决方案:

  1. 简短的回答是:没有获取种子的标准方法

  2. 最接近的解决方法是在开头保存 INITIAL 种子,并计算调用 rand() 函数的次数。我将此标记为解决方案,因为它适用于每个编译器的当前std::rand()函数(这是主要问题)。我对我的 2.0 GHz CPU 进行了基准测试,发现我可以在 35 秒内调用&count rand() 1,000,000,000 次。这听起来不错,但我有 80,000 次调用来生成一个对象。这将代数限制为 50,000,因为 unsigned long 的大小。无论如何,这是我的代码:

    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. 另一种方法是实现您自己的伪随机数生成器,就像 Matteo Italia 建议的那样。这是最快的,也可能是最好的解决方案。您不限于 4,294,967,295 次rand()调用,也不需要使用其他库。值得一提的是,不同的编译器有不同的生成器。我已经将 Matteo 的LCG与Mingw/GCC 3.4.2 和 G++ 4.3.2 中的rand()进行了比较。他们三个都是不同的(种子= 0)。

  4. 使用 C++11 或 Cubbi、Jerry Coffin 和 Mike Seymour 建议的其他库的生成器。如果您已经在与他们合作,这是最好的主意。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() 的当前种子?

下面是一种特定于实现的方法来保存和恢复伪随机数生成器 (PRNG) 状态,该状态与 Ubuntu Linux 上的 C 库一起使用(在 14.04 和 16.04 上测试)。

#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. C 库使用相同的 PRNG 来公开rand()random()接口,以及
  2. 关于 C 库中此 PRNG 的默认初始化的一些知识(128 字节状态)。

如果运行,这应该输出:

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++11 PRNG )。

于 2016-09-15T22:50:33.920 回答
0

您可以尝试保存用于在 srand 之前(或之后)播种的值。

因此,例如:

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

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

这两个值应该相同。

于 2012-04-17T20:56:41.863 回答
0

我建议您使用Mersenne Twister Pseudo-Random Number Generator。它速度很快,并提供了非常好的随机数。您可以非常简单地在类的构造函数中播种生成器

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

然后,您只需将用于生成序列的种子存储在某处...

于 2014-02-21T04:17:44.430 回答