如何使用C#生成真正(非伪)随机数?

35

我知道 Random 类可以生成伪随机数,但是是否有一种方法可以生成真正的随机数?


1
什么是“真正的随机”?即使是掷骰子也不是完全随机的。掷骰子的结果将取决于高度、施加的力量、风等因素。你的大脑无法处理这些数据。 - Matthieu Charbonnier
12个回答

61

这里的答案有两个主要方面。有一些非常重要的微妙之处需要您注意...

简单方法(为了简单性和实用性)

RNGCryptoServiceProvider是BCL中的Crypto API的一部分,应该可以为您完成工作。它仍然是技术上的伪随机数生成器,但“随机性”的质量更高 - 适用于加密目的,正如名称所示。

还有其他具有高质量伪随机生成器的加密API可用。例如Mersenne twister算法非常受欢迎。

与BCL中的Random类相比,它要好得多。例如,如果在图表上绘制由Random生成的数字,则应该能够识别出模式,这是弱点的明显标志。这在很大程度上是因为该算法仅使用固定大小的种子查找表。

困难方法(用于高质量理论随机性)

要生成真正的随机数,您需要利用一些自然现象,例如核衰变,微小的温度波动(CPU温度是一个相对方便的来源),等等。但是,这要困难得多,并且需要额外的硬件。我怀疑实际解决方案(RNGCryptoServiceProvider或类似解决方案)应该可以完美地为您完成工作。

现在,请注意,如果您确实需要真正的随机数,您可以使用像Random.org这样的服务,它使用非常高的随机性/熵(基于大气噪声)生成数字。 数据可供免费下载。 尽管如此,对于您的情况来说,这可能是不必要地复杂,尽管它肯定为科学研究等提供了适合的数据。
最终选择取决于您自己,但至少现在您应该能够做出有信息的决策,了解各种类型和级别的RNGs。

12
谢谢您的提示,我会尝试使用RNGCryptoServiceProvider。我也可能会尝试一些核衰变... - Max
2
@Douglas - 如果你从来没有得到过重复的数字,那就意味着它不是完全随机的。 - Jim Raden
5
我刚刚在首页上尝试了那个生成器,得到了25和43这两个完全无关的数字!这种惊人的情况令我毛骨悚然。 - Daniel Earwicker
1
从技术上讲,这是一个伪随机数生成器(PRNG)。这并不是一个技术细节 - 在使用真正的随机数生成器和伪随机数生成器之间存在根本的区别。 - Nick Johnson
3
@Nick: 我之所以说“技术上简单”是因为PRNG经常只被称为RNG……但是,我认为我已经清楚地区分了这两种类型及其方法。 - Noldorin
显示剩余5条评论

14

简短回答:仅使用C#(即仅使用纯数学构造),不可能直接生成真正的随机数。

长一点的回答:只有通过使用能够产生“随机性”的外部设备,如白噪声发生器或类似设备来捕获该设备的输出作为伪随机数生成器(PRNG)的种子才可能实现。这一部分可以使用C#完成。


1
+1 - 抓住了问题的本质。根据定义,纯算法生成的任何东西都不能真正地随机。您需要一个外部的模拟随机源,例如测量宇宙背景辐射的设备。 - ConcernedOfTunbridgeWells
1
很有趣的是,学习到计算机无法产生看似如此简单的东西。他们不能使用其他元素,比如CPU温度吗? - Max
1
@Max:你说得一点没错。请看我在自己的帖子中的评论。然而,CPU温度仍然无法在所有机器上访问 - 这可能是服务器设备的好解决方案。 - Noldorin
2
@Max:随机性是那种看似简单的概念,但人工生成它(或数学上)却非常复杂,甚至可能是不可能的事情。 - Mike Dinescu
1
但是声称使用C#和“计算机”是不可能的说法并不正确。计算机是一个真实的物理系统,在其操作中具有很多随机性。只需要编写模拟监控设备来捕获它 - 例如,您可以将内置麦克风对准风扇并捕捉一些噪音。 - whatnick
显示剩余2条评论

4

只有真正随机的物理输入设备提供种子才能生成真正的随机数。

科学界仍在争论是否存在物理上真正的随机现象(并且可能会持续很长时间)。

伪随机数生成器是次优选择,而最好的伪随机数生成器非常难以预测。


4

正如约翰·冯·诺伊曼开玩笑所说,“任何考虑使用算术方法生成随机数字的人,当然是处于一种罪恶状态。”


3

这篇文章虽然有点老,但是我还是想继续讨论一下。这样做是为了保证完整性,让人们了解一些关于C#中随机数的知识。

如果要真正地获得随机数,最好的方法就是使用“安全伪随机生成器”(如salsa20或RC4)。它们通过了一系列测试,其中“高效”的对手试图将它们与随机数区分开来。这种方法也有一定的代价,对于大多数用途来说可能并不必要。

C#中的Random类通常表现得很好,具有看起来随机的静态分布。但是random()的默认种子是系统时间。因此,如果在“同一时间”取很多个随机数,则它们使用相同的种子并且会是相同的(“随机”是完全确定性的,不要被它所迷惑)。由于Random类的缺陷,类似的系统时间种子也可能产生相似的数字。

解决这个问题的方法是设置自己的种子,例如:

Random random = new Random((int)DateTime.Now.Ticks & (0x0000FFFF + x));

假设你已经创建了一个循环以获得大量随机数,那么x是你递增的一些值。

此外,在c#中,对于你的新变量使用类似NextDouble()的随机扩展函数可以帮助操作随机数。在这种情况下,将它们强制转换到区间(0,1)中变为unif(0,1),这是一个您可以插入到统计公式中创建所有统计分布的分布。


2
回顾一下,这个答案似乎在暗示你要为许多随机数使用许多Random对象。实际上不需要,你只需使用一个Random对象并频繁地调用random.next方法即可。就记录而言,我认为new Random(Guid.NewGuid().GetHashCode()) 是我最喜欢的,尽管它真的无关紧要。 - Nathan Cooper

2
请看使用像YarrowFortuna这样的算法来进行熵积累。这些算法的重点在于,它们将熵作为一种理论信息内容的度量,可用于通过了解过去的数字和生成它们的算法来预测未来的数字;并且它们使用密码技术将新的熵源折叠到数字生成器中。
您仍然需要一个外部随机数据源(例如硬件随机数源),无论是时间、按键、鼠标移动、硬盘访问时间、CPU温度、网络摄像头数据、股票价格还是其他任何东西 - 但无论如何,您都要将此信息混合到熵池中,以便即使真正的随机数据速度慢或质量低,也足以以不可预测的方式继续运行。

1
只是为了澄清,所有声称C#或您的计算机上没有真正的随机数生成器的人都是错误的。多核处理器本质上是一个真正的随机数生成器。非常简单地通过利用处理器旋转,您可以生成没有可辨识模式的布尔值。从那里,您可以使用布尔值作为位并通过将位相加构造数字来生成所需的数字范围。
是的,这比纯数学解决方案慢得多,但纯数学解决方案总是会有一个模式。
public static bool GenerateBoolean()
{
    var gen1 = 0;
    var gen2 = 0;
    Task.Run(() =>
    {
        while (gen1 < 1 || gen2 < 1)
            Interlocked.Increment(ref gen1);
    });
    while (gen1 < 1 || gen2 < 1)
        Interlocked.Increment(ref gen2);
    return (gen1 + gen2) % 2 == 0;
}

1

计算机中不存在真正的随机数,一切都基于其他事物。为了生成伪随机数据,可以尝试使用诸如硬盘温度、CPU温度、网络使用情况(每秒钟的数据包数)以及可能的网站访问量等池子。


计算机中不存在真正的随机性,现实生活也是如此。除非你所谓的“随机”指的是“我现在无法预测的事情”。 - Matthieu Charbonnier

1
我正在考虑基于 Twitter 或其他社交网络之一构建随机数生成器。基本上,使用 API 拉取最近的帖子,然后使用它来种植高质量的伪随机数生成器。它可能并不比定时器随机化更有效,但似乎很有趣。此外,这似乎是大多数人在 Twitter 上发布内容的最佳用途。

这很可能具有与从时间戳播种相同的攻击向量。 - Bob Aman

1

我一直喜欢这个想法,因为它具有复古的60年代外观:

Lavarand


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接