【问题标题】:"Resetting" pseudo-random number generator seed multiple times?多次“重置”伪随机数生成器种子?
【发布时间】:2012-11-26 14:19:17
【问题描述】:

今天,我的朋友有一个想法,使用生成的伪随机数多次设置伪随机数生成器的种子,以“使事情更随机化”。

C# 中的示例:

// Initiate one with a time-based seed
Random rand = new Random(milliseconds_since_unix_epoch());
// Then loop for a_number_of_times...
for (int i = 0; i < a_number_of_times; i++)
{
    // ... to initiate with the next random number generated
    rand = new Random(rand.Next());
}
// So is `rand` now really random?
assert(rand.Next() is really_random);

但我认为这可能会增加将重复种子用于伪随机数生成器的机会。

这个

  1. 让事情更加随机化,
  2. 使其循环使用一定数量的种子,或
  3. 对随机性没有任何作用(即既不增加也不减少)?

有没有哪位伪随机数生成器方面的专家能给出详细的解释,好让我的朋友信服?我很高兴看到答案解释了一些伪随机数生成器算法的更多细节。

【问题讨论】:

  • ou... 您最好的选择是 (xkcd) 掷一次骰子,然后简单地将其用作常数。 Nahw 只是在开玩笑——计算机就是不能很好地生成随机数。每个非伪随机都使用外部(如物理测量)源进行初始化和随机性......所以恕我直言,在应用程序中使用 time.h 之类的其他东西(就像你已经做过的那样)是不值得的。如果您确实需要随机数,请考虑为这些使用硬件
  • @Najzero 好吧,只是想了解更多关于伪数发生器的信息,所以我不会为此购买核衰变探测器。 :)
  • ffffshhh...不再承诺,其他一切都差不多xkcd.com/221

标签: random


【解决方案1】:

伪随机数有三个基本使用级别。每个级别都包含它下面的级别。

  1. 没有特定相关性保证的意外数字。此级别的生成器通常具有一些隐藏的相关性,这些相关性可能对您很重要,也可能不重要。
  2. 具有已知非相关性的统计独立数字。这些通常是数值模拟所必需的。
  3. 无法猜测的加密安全数字。当存在安全问题时,这些都是必需的。

这些都是确定性的。随机数生成器是一种具有某种内部状态的算法。应用该算法一次会产生一个新的内部状态和一个输出数。播种生成器意味着设置内部状态;种子接口并不总是允许设置所有可能的内部状态。作为一个好的经验法则,始终假定默认库 random() 例程仅在最弱的级别(级别 1)运行。

要回答您的具体问题,问题 (1) 中的算法不能增加随机性,而 (2) 可能会降低随机性。因此,对随机性的期望严格低于一开始就播种一次。原因在于可能存在短迭代周期。函数F 的迭代循环是一对整数nk,其中F^(n) (k) = k,其中指数是应用F 的次数。例如,F^(3) (x) = F(F(F(x)))。如果迭代周期很短,随机数将比其他情况下更频繁地重复。在呈现的代码中,迭代函数是为生成器播种,然后获取第一个输出。

要回答一个您没有完全提出但与理解这一点相关的问题,使用毫秒计数器播种会使您的生成器无法通过第 3 级不可猜测性测试。这是因为可能的毫秒数是加密的小,这是一个已知可以进行详尽搜索的数字。在撰写本文时,应将 2^50 视为密码学上的小数。 (对于任何年份的密码量大,请找有信誉的专家。)现在一个世纪的毫秒数大约是 2^(41.5),所以不要为了安全目的而依赖这种形式的播种。

【讨论】:

    【解决方案2】:

    您的示例不会增加随机性,因为entropy 没有增加。它只是从程序的执行时间推导出来的。

    计算机不是使用基于当前时间的东西,而是维护一个熵池,并使用统计上随机(或至少不可猜测)的数据构建它。例如,网络数据包之间的时间延迟、击键或硬盘读取时间。

    如果你想要好的随机数,你应该利用那个熵池。这些被称为 Cryptographically secure pseudorandom number generators

    在 C# 中,请参阅 Cryptography.RandomNumberGenerator Class,了解获取安全随机数的正确方法。

    【讨论】:

      【解决方案3】:

      这不会让事情变得更加“随机”。

      我们的种子决定了 rand.next() 给我们的看起来随机但完全确定的数字序列。

      您的代码不会让事情变得更加随机,而是定义了从初始种子到某个最终种子的映射,并且,给定相同的初始种子,您将始终得到相同的最终种子。

      尝试使用这段代码,你会明白我的意思(还有,here is a link to a version you can run in your browser):

      int my_seed = 100; // change my seed to whatever you want
      Random rand = new Random(my_seed);
      for (int i = 0; i < a_number_of_times; i++)
      {
          rand = new Random(rand.Next());
      }
      // does this print the same number every run if we don't change the starting seed?
      Console.WriteLine(rand.Next()); // yes, it does
      

      带有这个最终种子的 Random 对象就像任何其他 Random 对象一样。只是花了你更多的时间来创建它。

      【讨论】:

      • 但是使用当前时间怎么样(比如milliseconds_since_unix_epoch())?
      • 是的,使用milliseconds_since_unix_epoch() 是生成初始种子的正确方法。在您的代码中,最终的随机对象将具有一个种子,该种子是该初始种子的可预测转换。我提供的代码的重点是向您展示,通过执行这种可预测的转换,您不会再向系统引入任何熵。
      猜你喜欢
      • 1970-01-01
      • 2010-11-01
      • 1970-01-01
      • 2011-02-08
      • 1970-01-01
      • 2011-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多