【问题标题】:How to properly initialize a C++11 std::seed_seq如何正确初始化 C++11 std::seed_seq
【发布时间】:2014-04-26 16:47:16
【问题描述】:

我有一个 C++11 程序,它需要创建几个独立的随机生成器,供不同线程在并行计算中使用。这些生成器应该使用不同的种子值进行初始化,以便它们都产生不同的伪随机序列。

我看到有一个 std::seed_seq 类似乎是为此目的,但目前尚不清楚构建一个的正确方法是什么。我见过的例子,比如the one on cppreference.com,用程序中硬编码的几个整数常量来初始化它:

std::seed_seq seq{1,2,3,4,5};

我怀疑这实际上是推荐的最佳做法,所以我想知道推荐的做法是什么。特别是:

  • 既然seed_seq 可以用任意数量的整数来初始化,那么它的初始化列表的长度有什么意义呢?如果我想为 100 个随机生成器生成种子,是否需要用 100 个整数初始化我的 seed_seq?
  • 如果初始化列表的长度不必与我打算生成的种子数量相匹配,是否可以仅使用 一个 整数初始化 seed_seq,然后将其用于产生大量种子?
  • 如何使用 no 整数进行初始化,即使用默认构造函数? (当然,这意味着我每次都会得到相同的种子。)
  • 如果可以从单个整数构造seed_seq,然后从中生成大量种子,那么使用seed_seq 而不是普通的随机生成器有什么好处?为什么不直接从那个整数构造一个 std::mt19937 并使用 that 为其他生成器生成种子值?

【问题讨论】:

    标签: c++ c++11 random


    【解决方案1】:

    根据C++11标准(26.5.7.1.8节),seed_seq可以生成一个序列,该序列很可能是由哈希函数生成的,在范围内均匀随机。

    我尝试回答以下问题:

    Q1“既然可以用任意数量的整数初始化一个seed_seq,那么它的初始化列表的长度有什么意义呢?如果我想为100个随机生成器生成种子,我需要用100个整数来初始化我的seed_seq吗? ?”

    A1。你不需要用很多整数初始化seed_seq。即使用一个随机整数初始化的seed_seq,生成的序列也保持随机性。但是你用更多的整数和更宽的范围初始化seed_seq,生成的序列更不容易被攻击者“碰撞”。

    第二季度。 “如果初始化列表的长度不必与我打算生成的种子数量相匹配,是否可以只用一个整数初始化一个seed_seq,然后用它来产生大量的种子?”

    A2。是的,如果您不需要加密安全级别,可以只用一个整数初始化 seed_seq。

    第三季度。 “如何不使用整数进行初始化,即使用默认构造函数?(这意味着我每次都会得到相同的种子,当然。)”

    A3。通过默认构造的seed_seq 运行更多,您将获得相同的序列。因此它将成为一个安全漏洞。

    第四季度。 “如果可以从单个整数构造一个seed_seq,然后从中生成大量种子,那么使用seed_seq 而不是普通的随机生成器有什么好处?为什么不从那个单个整数构造一个std :: mt19937并使用它为其他生成器生成种子值?”

    A4。 seed_seq 是一种轻量级算法,只对填充的序列进行 3 次迭代。我猜你可以使用其他随机生成器代替seed_seq。

    【讨论】:

      【解决方案2】:

      使用这样的固定序列的问题在于,您会从中得到相同的种子序列,就像您在程序开始时调用 srand(42) 一样:它会生成相同的序列。

      C++11 标准状态(在26.5.7.1 Class seed_seq 部分):

      种子序列是一个对象,它消耗整数值数据序列并根据消耗的数据生成请求数量的无符号整数值 i, 0 i 32。 p>

      [注意:这样的对象提供了一种机制来避免随机变量流的复制。这可能很有用,例如,在需要大量随机数引擎的应用程序中。 ——尾注]

      它还在该部分的8 段中说明如何将这些整数转换为种子,即使整数输入项非常相似,这些种子的分布也是可以接受的.因此,您可能可以将其视为种子值的伪随机数生成器。

      如果项目本身具有一定的随机性,更多的项目将在种子值中提供更多的“随机性”。出于这个原因,使用常量作为输入是一个坏主意。

      我倾向于使用srand (time (0)) 随机化one 生成器的方式非常相似。换句话说:

      #include <random>
      #include <cstdint>
      #include <ctime>
      #include <iostream>
       
      int main()
      {
          std::seed_seq seq{time(0)};
          std::vector<std::uint32_t> seeds(10);
          seq.generate(seeds.begin(), seeds.end());
          for (std::uint32_t n : seeds) {
              std::cout << n << '\n';
          }
      }
      

      如果您有多个随机源,例如在 Linux 下从 /dev/random 读取的值,或者某种描述的白噪声生成器,或者最后一次按键之间的平均毫秒数用户运行此程序,您可以将其用作额外输入:

      std::seed_seq seq{time(0), valFromDevRandom(), getWhiteNoise(), avgMillis()};
      

      但我怀疑常数是要走的路,因为它们不会给方程式增加随机性。

      【讨论】:

      • 嗯……用多个熵源初始化是有道理的,我猜用一个整数初始化应该不会比用一个整数播种普通随机生成器差。
      • 该标准实际上指定了std::seed_seq 使用的确切算法(参见rand.util.seedseq 第8 段),而I'm told 该算法是随机数生成器的预热序列。
      • @bames53,嗯,你回答中的那个例子看起来我应该为每个发电机使用一个完全独立的seed_seq,以确保每个发电机的初始状态完全预热。我在想我会使用一个 seed_seq 来为每个生成器生成一个种子整数。
      • 也许我应该从一个合适的熵源创建一个seed_seq,并使用它为每个线程生成一个种子整数,然后在每个线程内,使用整数创建另一个 seed_seq 用于为线程的生成器播种。
      • @Wyzard,或者您可以更进一步,使用最后一个seed_seq 的输出来实例化另一个seed_seq,从中提取用于实例化实际数字生成器的值。在某些时候,你必须停下来开始使用数字:-)
      猜你喜欢
      • 2012-12-20
      • 2013-09-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-08
      相关资源
      最近更新 更多