六年来,我的网站上一直有一个随机数生成器页面。很长一段时间以来,它是 Google 上“随机数生成器”的第一个或第二个结果,并且已被用来决定讨论论坛和博客上的数十个甚至数百个竞赛和绘图(我知道,因为我在我的网站中看到了推荐人)网络日志,通常会去看一下)。
今天,有人发邮件告诉我它可能不像我想象的那么随机。她尝试生成非常大的随机数(例如,1到10000000000000000000之间),发现它们几乎总是相同的位数。事实上,我将函数封装在一个循环中,这样我就可以生成数千个数字,而且果然,对于非常大的数字,变化只有大约 2 个数量级。
为什么?
这里是循环版本,大家可以自己尝试一下:
http://andrew.hedges.name/experiments/random/randomness.html
它既包括来自 Mozilla Developer Network 的简单实现,也包括我从 1997 年从不再存在的网页上删除的一些代码(Paul Houle 的“Central Randomizer 1.3”)。查看源代码以了解每种方法的工作原理。
我读过 here 和 elsewhere 关于 Mersenne Twister。 我感兴趣的是为什么 JavaScript 内置 Math.random 函数的结果不会有更大的变化。谢谢!
给出 1 到 100 之间的数字。
给出 1 到 1000 之间的数字。
等等。
因此,如果您随机选择一些数字,那么绝大多数选定的数字将具有相同的位数,因为绝大多数可能的值具有相同的位数。
您的结果实际上是预期的。如果随机数均匀分布在 1 到 10^n 的范围内,那么您预计大约 9/10 的数字有 n 位,另外 9/100 有 n-1 位数字。
有不同类型的随机性。 Math.random 为您提供均匀的数字分布。
如果您想要不同的数量级,我建议使用指数函数来创建所谓的幂律分布:
function random_powerlaw(mini, maxi) {
return Math.ceil(Math.exp(Math.random()*(Math.log(maxi)-Math.log(mini)))*mini)
}
此函数应该为您提供与 2 位数和 3 位数大致相同数量的 1 位数。
随机数还有其他分布,例如正态分布(也称为高斯分布)。
对我来说看起来完全随机! (提示:它取决于浏览器。)
就我个人而言,我认为我的实现会更好,尽管我是从XKCD那里偷来的,谁应该始终得到承认:
function random() {
return 4; // Chosen by a fair dice throw. Guaranteed to be random.
}
以下论文解释了主要 Web 浏览器中的 math.random() 如何安全(不安全): “主要浏览器中的临时用户跟踪和跨域信息 Amid Klein (2008) 的“泄漏和攻击”。它并不比典型的 Java 或 Windows 内置 PRNG 函数强。
另一方面,实现周期 2^19937-1 的 SFMT 需要为每个 PRNG 序列维护 2496 字节的内部状态。有些人可能认为这是不可原谅的成本。
如果您使用像 100000000000000000000 这样的数字,您将超出 Javascript 使用的数据类型的准确性。请注意,生成的所有数字均以“00”结尾。
我在Chaos Game上尝试了JS伪随机数生成器。
我的 Sierpiński 三角形 说它非常随机:
好吧,如果您生成的数字最多为 1e6,您将有望以大致相等的概率获得所有数字。这也意味着您只有十分之一的机会获得少一位数的数字。百分之一的机会减少两位数,等等。我怀疑您在使用另一个 RNG 时会看到很大的差异,因为您在数字上有均匀分布,而不是它们的对数。
从 1 到 N 均匀分布的非随机数具有相同的性质。请注意(在某种意义上)这是一个精度问题。 0-99(作为整数)的均匀分布确实有 90% 的数字具有两位数。 0-999999 上的均匀分布有 905 个数字为五位数。
任何一组数字(在一些不太严格的条件下)都有密度。当有人想要讨论“随机”数字时,应该指定这些数字的密度(如上所述)。常见的密度是均匀密度。还有其他密度:指数密度、正态密度等。在提出随机数生成器之前,必须选择相关的密度。此外,来自一种密度的数字通常可以通过各种手段轻松转换为另一种密度。