【问题标题】:How to get current seed from C++ rand()?如何从 C++ rand() 获取当前种子?
【发布时间】:2012-04-29 05:41:14
【问题描述】:

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

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

Google 没有给我任何积极的线索。讲 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() 调用,也不需要使用其他库。值得一提的是,不同的编译器有不同的生成器。我在 Mingw/GCC 3.4.2 和 G++ 4.3.2 中比较了 Matteo 的 LCGrand()。所有 3 个都是不同的(seed = 0)。

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

【问题讨论】:

  • 如果在解决方案 2 中只有计数限制是问题,您可以添加另一个 unsigned long 计数器来计算溢出。这将有效地使您的计数器的位大小增加一倍,并且当然可以进一步扩展。
  • 顺便说一句,你很高兴添加一个解决方案摘要(旧的 usenet 传统好?)。
  • 嘿,不。我实际上不得不用谷歌搜索 usenet 的含义:)。只是觉得提供一些反馈会很好,这些答案很有用。

标签: c++ random get seed


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • 如果您想重新创建整个序列,这很好,但如果您想在任何给定时间提取种子,则不是。
      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

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

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

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

        【讨论】:

          【解决方案6】:

          有没有办法在任何给定时间复制 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. 有关此 PRNG 在 C 库中的默认初始化的一些知识(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适当地支持这一点)。

          【讨论】:

            【解决方案7】:

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

            所以,例如:

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

            这两个值应该相同。

            【讨论】:

              【解决方案8】:

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

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

              那么您只需将用于生成序列的种子存储在某个地方...

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2013-09-28
                • 2012-06-06
                • 1970-01-01
                • 2021-09-04
                • 1970-01-01
                相关资源
                最近更新 更多