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

时间:2012-04-17 20:37:54

标签: c++ random get seed

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

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

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

有没有人知道实施种子窃取器的其他随机数生成器?


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

解:

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

  2. 最接近的解决方法是在开头保存INITIAL种子,并计算调用rand()函数的次数。我将其标记为解决方案,因为它适用于每个编译器的当前 std :: rand()函数(这是关于的主要问题)。我已经对我的2.0 GHz CPU进行了基准测试,发现我可以在35秒内调用& count rand() 1,000,000,000次。这可能听起来不错,但我有80,000个调用来生成一个对象。由于unsigned long的大小,这将生成的数量限制为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个都不同( seed = 0 )。

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

8 个答案:

答案 0 :(得分:13)

  

有没有人知道实施种子窃取者的其他随机数生成器

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

答案 1 :(得分:8)

获取当前种子没有标准方法(您只能通过srand设置),但您可以自行重新实现rand()(通常为linear congruential generator)几行代码:

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;
    }
};

答案 2 :(得分:5)

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

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

答案 3 :(得分:5)

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

答案 4 :(得分:4)

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

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

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

答案 5 :(得分:1)

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

所以,例如:

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

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

两个值应该相同。

答案 6 :(得分:1)

  

有没有办法在任何给定时间复制rand()的CURRENT种子?

以下是一种特定于实现的方法,用于保存和恢复与Ubuntu Linux上的C库一起使用的伪随机数生成器(PRNG)状态(在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字节状态)。
  3. 如果运行,则应输出:

    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正确支持这一点。)

答案 7 :(得分:0)

我建议你使用Mersenne Twister Pseudo-Random Number Generator。速度快,提供非常好的随机数。您可以通过

非常简单地在类的构造函数中生成生成器
unsigned long rSeed = 10;
MTRand myRandGen(rSeed);

然后你只需要存储用于生成序列的种子......