如何从 C++ rand() 获取当前种子

How to get current seed from C++ rand()?

本文关键字:获取 种子 C++ rand      更新时间:2023-10-16

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

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

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

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


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

解决 方案:

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

  2. 最接近的解决方法是在开始时保存 INITIAL 种子,并计算您调用 rand() 函数的次数。我将其标记为解决方案,因为它适用于每个编译器的当前 std::rand() 函数(这是主要问题)。我对我的 2.0 GHz CPU 进行了基准测试,发现我可以在 35 秒内调用和计数 rand() 1,000,000,000 次。这听起来不错,但我有 80,000 次调用来生成一个对象。这会将代数限制为 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. 另一种方法是实现自己的伪随机数生成器,就像Matteo Italia建议的那样。这是最快的,也可能是最好的解决方案。您不限于 4,294,967,295 次 rand() 调用,也不需要使用其他库。值得一提的是,不同的编译器有不同的生成器。我已经将Matteo的LCG与Mingw/GCC 3.4.2和G ++ 4.3.2中的rand()进行了比较。所有 3 个都不同(种子 = 0)。

  4. 使用来自C++11或其他图书馆的生成器,如Cubbi,Jerry Coffin和Mike Seymour建议的那样。如果您已经在与他们合作,这是最好的主意。C++11 发电机的链接:http://en.cppreference.com/w/cpp/numeric/random(这里也有一些算法说明)

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

所有标准 C++11 随机数生成器(在 TR1 和 Boost 中也可用)都提供此功能。您可以简单地复制生成器对象或序列化/反序列化它们。

没有

标准方法来获取当前种子(您只能通过 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;
    }
};

使用 srand() 设置种子。 保存用作种子的值。

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

C++11 中的随机数生成类支持operator<<存储其状态(主要是种子),并operator>>将其读回。所以,基本上,在创建对象之前,保存状态,然后当你需要重新生成相同的序列时,重新读取状态,然后就可以了。

rand()不提供

任何提取或复制种子的方法。你能做的最好的事情就是在用srand()设置种子时存储种子的初始值,然后从中重建整个序列。

Posix 函数rand_r()可让您控制种子。

C++11 库包括一个基于序列生成"引擎"的随机数库;这些引擎是可复制的,并允许使用 <<>> 运算符提取和恢复其状态,以便您可以随时捕获序列的状态。TR1 和 Boost 中提供了非常相似的库,如果你还不能使用 C++11。

有没有办法在任何给定时间复制 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 库用于公开 rand()random() 接口的相同 PRNG,以及
  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 来正确支持此功能)。

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

因此,例如:

int seed = time(NULL);
srand(time(NULL));
cout << seed << endl;
cout << time(NULL);

这两个值应相同。

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

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

然后你只需要把你用来生成序列的种子存储在某个地方......