JavaScript的Math.random有多随机?

127
我网站上有一个 随机数生成器 页面已经有6年了。很长一段时间,它在谷歌搜索“随机数生成器”时排名第一或第二,并被用于决定许多讨论论坛和博客上的比赛和抽奖活动(我知道这是因为我在我的网络日志中看到了引荐者并通常会去看一下)。
今天,有人给我发电子邮件告诉我它可能不像我想象的那样随机。她试图生成非常大的随机数(例如,在1和10000000000000000000之间),并发现它们几乎总是相同的数字位数。事实上,我将函数包装在一个循环中,以便可以生成数千个数字,而对于非常大的数字,变化仅约为2个数量级。 为什么?
这是循环版本,所以您可以自己尝试:

http://andrew.hedges.name/experiments/random/randomness.html

这包括了一个简单的实现,取自Mozilla开发者网络,以及一些来自1997年的代码,我从一个不再存在的网页上偷走了它(Paul Houle的“中央随机器1.3”)。查看源代码以了解每种方法的工作方式。

我在这里其他地方读到了有关Mersenne Twister的信息。我感兴趣的是,为什么JavaScript内置的Math.random函数的结果变化不会更大。谢谢!


1
被萨纳特(sarnath)抢先了 - maetl
7
如果您正在寻找标题中问题的答案,请参阅https://dev59.com/T3E95IYBdhLWcg3wXcrd - Andrew B.
从标题上看,我以为这个问题是关于Math.random()方法的分布均匀性。但是...以某种方式来说,这是出乎意料的。坦白地说,我无法相信有一百多个人不知道这个问题的原因。 - Константин Ван
9个回答

197

在1至100之间的数字中:

  • 有9个数字只有1位数(1-9)
  • 有90个数字只有2位数(10-99)
  • 只有1个3位数(100)

在1至1000之间的数字中:

  • 有9个数字只有1位数
  • 有90个数字只有2位数
  • 有900个数字只有3位数
  • 只有1个4位数

依此类推。

因此,如果你随机选择一些数字,那么绝大多数的选中数字将具有相同数量的位数,因为大多数可能的值都具有相同数量的位数。


13
你关于随机性的概念认为其意味着完全和均匀分布,这很有趣。 - Roger Pate
26
“@R.Pate - 随机数生成并没有什么用,除非它在一个长时间尺度上均匀分布。” - annakata
3
请重新阅读。@David只是陈述了在限制范围内有哪些数字,而不是选择N个随机数字的结果。我承认标题有误导性。 - nikc.org
我修改了我的初始回答,使其表述更加明确。 - Quentin
3
记录一下,我点赞了这个回答和@jwoolard的回答。我选择将其设为采纳的答案,因为其中的例子非常清晰地说明了数字分布为什么会偏向于更多位数的数字。 - Andrew Hedges
1
@andrew-hedges 很正确 - 这是一个更清晰的答案,但还是谢谢 :) - jwoolard

59

你的结果其实是符合预期的。如果随机数在1到10^n的范围内均匀分布,那么你会期望大约9/10的数字有n位数,另外还有9/100的数字有n-1位数。


8
没问题。数字位数的分布预计会呈偏斜状态,但数字位数的对数分布应该是均匀的。 - Noldorin

48

有不同类型的随机性。 Math.random 会给你一个数字的均匀分布。

如果你想要不同数量级的随机数,我建议使用指数函数来创建所谓的幂律分布

function random_powerlaw(mini, maxi) {
    return Math.ceil(Math.exp(Math.random()*(Math.log(maxi)-Math.log(mini)))*mini)
}

这个函数应该会给你大致相同数量的一位数字、两位数字和三位数字。

还有其他用于随机数生成的分布,例如正态分布(也称为高斯分布)。


使用这个算法时,我将 minimum = 1maximum = 10,有时会得到 11 作为结果。你可能想使用 Math.floor 而不是 Math.round - Sam Eaton
1
为什么它能工作?它是否将均匀分布转换为指数分布? - shinzou
@shinzou 我在 math.stackexchange 上提问并得到了一个稍微不同的公式作为答案。我已经更改了代码以反映从 math.stackexchange 推导出的数学公式。 - Christian
你知道这个算法结果偏斜的原因吗?在 1 到 1,000,000 的范围内进行测试时,经过多次测试得到的平均值约为 76,300。当然,适当的范围随机化应该在 500,000 左右。 - Timothy C. Quinn

26

对我来说看起来完全是随机的!(提示:这与浏览器有关。)

个人认为我的实现会更好,尽管我是从XKCD窃取的,但应该始终给予他们荣誉。

function random() {
  return 4; // Chosen by a fair dice throw. Guaranteed to be random.
}

23
提到浏览器依赖性加一分,未附带xkcd链接减一分。 - Roger Pate
无论是否需要,由于这是xkcd,它都会被归属。 :) - Arafangion
2
我很惊喜也很高兴,“XKCD”这个词竟然成为本周大学挑战赛的一个问题的答案 :D - Matt Sach
2
Bergi:仅提供一个直接链接不够吗? - Arafangion
我认为他们的意思是这个笑话引用不正确(应该是“return 4;”而不是“random = 4;”)。 - Eren Tantekin

19

2
+1:提到的论文非常棒,远远超出了原问题的范围。 - Roland Illig

6

如果您使用类似于10000000000000000000这样的数字,那么您将超出Javascript使用的数据类型的精度范围。请注意,所有生成的数字都以“00”结尾。


1
然而在这种情况下,那不是他的问题。 - Joey
3
@Johannes - 这是他的其中一个问题 :) - annakata
IEE754的分布并不均匀。也许你可以以两个为增量表示0到999,这样就有足够的精度,如果你多次选择数字,就会注意到该范围内的分布是均匀的。10%将是两位数,90%将是三位数。但当你开始达到非常高的数字时,增量将超过1。你可能只能从一万亿步进到一万亿一千,而不能到一万亿零一。虽然对于小数字/规模来说,这种效应将是微不足道的或不存在的。但规模效应将产生更大的影响。 - jgmjgm

4

2
你介意在这里分享三角形代码和jsfiddle/jsbin吗?这样我们就可以轻松地在不同的浏览器中进行实践检查。 - Fabrício Matté
1
好的,但请给我几天时间,因为我需要将代码翻译成英文。现在它是波兰语和英语混合的,我有很多工作要做。 - zie1ony
1
@zie1ony,几天已经过去了。 - trusktr
仍在等待那个 jsfiddle! - airstrike
2
usp :( 工作,工作,工作 链接:http://kubaplas.vot.pl/green/fractal/ 第一个参数是顶点数。第二个参数是线段交点(从0到1)。只需尝试实验即可。 - zie1ony
5
链接失效了,也许可以提供一个Github仓库的链接代替? - Mark K Cowan

3

如果你生成的数字达到了1e6,那么你希望所有数字的概率大致相等。这也意味着你只有十分之一的机会得到一个比原来少一位数的数字。有百分之一的机会得到比原来少两位数的数字,以此类推。我怀疑使用另一个随机数生成器也不会有太大的区别,因为你在数字上有均匀分布,而不是它们的对数。


1

在1到N之间均匀分布的非随机数字具有相同的属性。需要注意的是(在某种意义上),这是精度问题。在0-99(作为整数)上的均匀分布确实有90%的数字具有两位数。在0-999999上的均匀分布中,有905个数字具有五位数。

任何一组数字(在一些不太严格的条件下)都有一个密度。当有人想要讨论“随机”数字时,应该指定这些数字的密度(如上所述)。常见的密度是均匀密度。还有其他密度:指数密度,正态密度等。在提出随机数生成器之前,必须选择相关的密度。此外,来自一个密度的数字通常可以通过各种手段轻松地转换为另一个密度。


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