【问题标题】:Seeding Multiple Random Number Generators播种多个随机数生成器
【发布时间】:2016-07-22 11:22:17
【问题描述】:

我最近在另一篇文章的 cmets 中讨论了多个相同类型的随机数生成器的初始化,在那次讨论中我们提出了以下问题:

1) 用不同的种子创建同一个随机数生成器的多个实例并在程序的不同部分使用这些随机数生成器是个好主意吗?

2) 特别是,使用 .Net Random 类创建随机数生成器(种子如下)以及在不同程序上下文中使用每个 RNG 的技术是否会导致问题:

int size = 64;  // The number of RNGs to use
int seed;       // Get seed using some normal technique
Random[] r = new Random[size];

for (int i = 0; i < size; i++)
{
    r[i] = new Random(seed + i);
}

3) 如果需要多个随机数流,您会推荐什么?

4) 当需要线程安全时,您建议如何生成随机数?

【问题讨论】:

标签: c# .net random thread-safety


【解决方案1】:

1) 创建具有不同种子的同一随机数生成器的多个实例并在程序的不同部分使用这些随机数生成器是否是个好主意?

没有。一般不推荐上述方案。

在他的著作《计算机编程艺术,第 2 卷:半数值算法》中。 Addison-Wesley,Reading,MA,第三版,1997,Knuth 博士指出

发明一个万无一失的随机数来源并不容易。

在这种情况下,我指出从随机序列中获取子序列的随机性可能比原始随机数序列的随机性小:

请注意,Micosoft 的 Random 实现基于减法滞后斐波那契生成器:

这种随机数生成器以内置的三点相关性而闻名,毕竟,我们正在生成下一个随机数:

这些类型的随机数生成器还严重依赖于初始 55 数字状态的初始化。糟糕的初始化可能会导致糟糕的随机数。在上述情况下,类似的状态可能会导致来自不同随机数生成器中的每一个的相关随机数。微软甚至在他们关于 System.Random 的 MSDN 帖子中建议不要这样做:MSDN The System.Random class and thread safety:

我们建议您创建单个 Random 实例来生成应用所需的所有随机数,而不是实例化单个 Random 对象。

我们将看一个示例,其中特定的初始化会在不同的随机数生成器之间产生强相关性并寻找替代方案。

2) 我已经实现了一个程序,该程序尝试如上所述初始化 64 个 Random 实例,以便我们观察任何可见的缺陷。我选择了一个特定的初始化作为概念证明:

int size = 64;    // The number of random numbers generators
int length = 20;  // The number of random numbers from each generator
int steps = 18;   // Move 18 steps forward in the beginning to show a particular phenomenon

Random[] r = new Random[size];

for (int i = 0; i < size; i++)
{
     r[i] = new Random(i + 1);

     // move RNG forward 18 steps
     for (int j = 0; j < steps; j++)
     {
          r[i].Next(3);
     }
}


for (int i = 0; i < size; i++)
{
     for (int j = 0; j < length; j++)
     {
          Console.Write(r[i].Next(3) + ", ");  // Generate a random number, 0 represents a small number, 1 a medium number and 2 a large number
     }

     Console.WriteLine();
}

这个程序生成这里显示的输出,每一行代表另一个RNG的输出:

请注意突出显示的列:在特定位置,RNG 似乎同步并产生看起来并不相互独立的输出。

我还想补充一点,创建一个随机数列表并从每一行的列表中获取一个随机数也会产生看起来很差的随机数(已知这里使用的 RNG 在某些统计数据之后失败)全部!)。

3) 使用的 RNG 类型取决于您的上下文。有些人可能对上述输出感到满意。在其他情况下,所使用的 RNG 可能无法使用(蒙特卡洛模拟和密码学是两种情况,其中 System.Random 应该从不使用,即使对于一个随机数流也是如此)。

如果您需要提取随机数的多个子序列,请查找专为此目的设计的 RNG:

4) 最后,如果我想在多个线程中使用 System.Random 怎么办? Microsoft MSDN 在我上面提到的同一个链接中有答案:

【讨论】:

  • 这比您的任何 cmets 都更有说服力...虽然在任何时候只有 3 个数字可供选择,但这并不相当那么令人惊讶。 (例如,如果您使用 Next(100) 显示相同的模式,那就更令人惊讶了。
  • @JonSkeet 这已经足够令人惊讶了。这个想法是显示高、中和低数字。 RNG 决定“同时”输出大量数字。在第二列之后,我错过了另一列可见的列。 13 个数字同时“小”的概率(如上图第 2 列所示)约为 150 万分之一。通常,您使用类似的随机数在几个动作之间进行选择或模拟掷骰子。 (另外,我永远不可能在评论中添加这么多的参考和信息——它们实际上是 cmets。)
  • 我从我的回答中添加了指向这篇文章的链接。这感觉就像使用连续种子一样多的问题。正如我在回答中发布的那样,另一种选择是使用“主”Random,每个线程锁定一次,以便为该线程的Random 生成种子。用你的测试代码尝试这种方法对我来说并没有显示相同的模式。您是否同意这是一种改进,或者您仍然从根本上反对这种方法?
  • 我相信有多个项目在起作用。连续的种子是个问题,是的。微软(见上面的链接)建议不要使用这种方法(由于输出中可能存在相关性),并建议使用类似“主”随机的方法,以避免导致上述问题。对于你的最后一个问题,这种方法在理论上是合理的(如果你有一个好的 RNG(也许通过多态性?),你会得到很好的结果),但是 Random 不是一个很好的 RNG,所以不幸的是你可能还会注意到一些不好的行为。未通过频谱测试的 RNG 将在此处产生较差的结果。
  • @JonSkeet 看看this old answer of mine,它确实使用一个随机播种第二个的确切模式,如果您首先使用无约束然后将第二个的输出限制为 0-200 它给出了非常,非常,可预测的第三个值rnd.Next(200)
【解决方案2】:

不确定“多个随机数流”是什么意思。在随机数中,任意两个随机数之间没有关系,没有顺序,每个都是一个独立的实例。

如果您使用加密 PRNG,则无需播种。考虑 .net RNGCryptoServiceProvider Class。

【讨论】:

  • 加密 PRNG 通常会不断地植入本质上随机的数据,例如 CPU 中断时间和其他实时事件,在某些情况下包括由于空气湍流导致的硬盘驱动器盘片速度的变化。下一个数字在任何测试中都不是完全确定的,并且输出无法与真正的随机性区别开来。这些不仅仅是花哨的线性同余生成器。在用户代码中产生更多的随机性,没有比这更好的方法了。
猜你喜欢
  • 1970-01-01
  • 2015-03-14
  • 2019-07-16
  • 1970-01-01
  • 2013-09-24
  • 2010-11-01
  • 2010-10-05
  • 2010-09-30
  • 2012-08-10
相关资源
最近更新 更多