提问人:Fabrício Matté 提问时间:1/12/2013 最后编辑:CommunityFabrício Matté 更新时间:10/25/2013 访问量:522
使用 Math.random() 获得通常排除的上限的几率
Odds to get the usually excluded upper-bound with Math.random()
问:
这可能看起来更像是一个数学问题,但由于它专门链接到 Javascript 的伪随机数生成器,我想它非常适合 SO。如果没有,请随时将其移至其他地方。
首先,我知道 ES 没有指定在伪随机数生成器中使用的算法 - -,但它确实指定了范围应该具有近似的均匀分布:Math.random()
15.8.2.14 随机 ( )
使用与实现相关的算法或策略,返回一个带有正号的 Number 值,该值大于或等于 0 但小于 1,该值是随机或伪随机选择的,在该范围内分布大致均匀。此函数不带任何参数。
目前为止,一切都好。现在,我最近偶然发现了来自 MDN 的这段数据:
请注意,由于 JavaScript 中的数字是 IEEE 754 浮点数,具有从舍入到最接近偶数的行为,因此这些范围(不包括本身的范围)并不精确,并且根据边界,在极少数情况下(大约为 1/2^62)可以计算通常排除的上限。
Math.random()
好。这让我进行了一些测试,结果(显然)在 Chrome 控制台和 Firefox 的 Firebug 上是相同的:
>> 0.99999999999999995
1
>> 0.999999999999999945
1
>> 0.999999999999999944
0.9999999999999999
让我们用一个简单的实际例子来说明我的问题:
Math.floor(Math.random() * 1)
考虑到上面的代码,IEEE 754 浮点数具有四舍五入到最接近偶数的行为,在范围均匀分布的评估下,我得出结论,它返回通常排除的上限(在我上面的代码中)的几率是 ,即大约 。Math.random()
1
0.000000000000000055555...
1/18,000,000,000,000,000
现在查看 MDN 数字,计算结果为 ,即比我的计算结果小 200 多倍。1/2^62
1/4,611,686,018,427,387,904
我做错了数学吗?Firefox 的伪随机数生成器是否分布不够均匀,无法产生这 200 倍的差异?
我知道如何解决这个问题,我知道这么小的几率甚至不应该被考虑在每天的使用中,但我很想知道这里发生了什么,以及我的数学是否被打破了或Mozilla的(我希望是前者)。 任何意见都是值得赞赏的。=]
答:
您不必担心将 Math.random() 中的数字四舍五入到 1。
当我在当前版本的 IE、Chrome 和 FF 中查看实现(从我得到的结果推断)时,有几个观察结果几乎可以肯定意味着您应该始终在 0 到 0.11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110.999999999999999944.toString(2)
铬:这里很简单。它通过生成 32 位数字并将其除以 1 << 32 来生成数字。(您可以看到始终返回整数)。(1 << 30) * 4 * Math.random()
FF:在这里,似乎总是生成的数字最多是 0.11......(53x 1),它实际上只使用小数点后 53 位。(您可以看到返回的不超过 53)。Math.random().toString(2).length - 2
IE浏览器:这里它与 FF 非常相似,只是如果小数点后的第一个数字是 0,并且这些数字肯定不会四舍五入为 1,则位数可能会更多。(您可以看到返回的不超过 53)。Math.random().toString(2).match(/1[01]*$/)[0].length
我认为(尽管我现在无法提供任何证据)任何实现都应该属于所描述的组之一,并且四舍五入到 1 没有问题。
评论
+1
非常有用的数据,谢谢。对于这个问题,我主要将其范围限定为Firefox实现,因为数据来自MDN,而其他JS实现(包括闭源实现)会有所不同。虽然拥有其他实现的数据很棒。 不过,有一件事,在你调用结果之前,已经遭受了四舍五入以适应 JS 的 53 位尾数,不是吗?=]
.toString(2)
Math.random()
Math.random()
0.999999999999999944
0.9999999999999999
0.999999999999999834
.toString(2)
评论
Math.random
=]
Math.pow(2, 62).toString(2)
.toString(2)
与数字比特无关,它是二进制数字系统转换。所以它甚至可以与等等Math.pow(2,1000).toString(2)
toString(2)
Math.pow(2,1000).toString(2).length
1001