Welcome

首页 / 软件开发 / 数据结构与算法 / 随机数问题

随机数问题2015-02-11当你在Stack Overflow网站标题中看到“随机”这个词你基本可以确定这是相同的基本问题无数的相似问题。本文带你探讨为什么随机性会引起这么多问题并且如何解决它们。

Stack Overflow (or newsgroup, or mailing list etc) )网站的问题通常是这样的:

我使用Random.Next生成随机数,但它一直给我相同的号码。 它不停的运行,但每次它会产生相同数量很多次。

这是由于这样的代码:

// Bad code! Do not use! for (int i = 0; i < 100; i++) { Console.WriteLine(GenerateDigit()); } .... static int GenerateDigit() { Random rng = new Random(); // Assume there"d be more logic here really return rng.Next(10); }
那么,这程序到底出了什么问题?

1.解读

这种Random类不是真正的随机数发生器,它是一个伪随机数发生器。任何Random实例都有一定量的状态,而当你调用Next( or NextDouble or NextBytes),它会使用该状态来返回到似乎是随机的数据,相应的改变它内部状态以便于在下一步调用时你将得到另一个伪随机数。

所有的这一切都是确定的,如果你开始一个Random的实例以相同的初始状态(可通过种子来提供),并使用相同的序列方法调用它,那你会得到相同的结果。

那么在我们的示例代码中到底出了什么问题? 我们使用的一个新的Random实例也在循环迭代。随机无参数的构造函数取当前日期和时间作为种子-在内部定时器工作之前你通常可以执行大量代码,当前的日期和时间就会发生变化。 因此,我们重复使用相同的种子就会重复得到相同的结果。

2.对此我们能做什么?

这个问题有很多的解决方案, 其中有些方法是比其他的更好。 让我们先挑出其中一种方法,因为它不同于其他的方法。

3.使用加密的随机数发生器

.NET有一个RandomNumberGenerator类应该是所有加密随机数生成器派生而来的抽象类。 这个框架本身附带了一个这样的派生类: RNGCryptoServiceProvider 。 加密随机数发生器的理念是,即使它可能仍然是一个伪随机生成器,它还是很难做到不可预料。 内置的实现需要多个熵源在你的电脑有效地呈现“噪音”,并难以预测。它可以使用这种噪音不仅仅是计算一个种子,也可以在生成下一个数字时让你知道当前的状态,这也许可能不足以预测下一个结果(或者那些已经生成),这主要取决于具体的实施。Windows也可以利用专业硬件资源的随机性(如一块硬件观察放射性同位素衰变),从而使得随机数发生器更加安全。

相比于这种随机,如果你看到(说)10个结果调用Random.Next(100)并投入大量计算资源任务,你可能会制定出最初的种子并预知接下来的结果将是...很有可能也会知道之前的结果是什么。 如果这种随机数应用于证券或金融的目的,这会是灾难性的事态。 加密随机数生成器通常比Random慢 ,但它在赋予数字难以预测和独立方面做得更好。

在很多情况下,随机数生成器的性能不是一个问题-但有一个适当的API就会出现问题。 随机数字生成器设计基础仅此是用来生成随机字节。比较这种API的随机 ,它可以让你请求一个随机整数,或随机double,或一组随机字节。我经常发现我需要一个整数的范围,得到可靠且一致地随机字节数组是很重要的。这不是不可能,但至少你可能会想要一个适配器类在随机数字生成器上。大多情况下,如果你能避免前面所述的陷阱,伪随机性的Random是可以接受的。

让我们看看如何能做到这一点。