使用浮点源统一分布整数

时间:2015-09-02 18:33:22

标签: javascript algorithm random floating-point statistics

在JavaScript中获取[0,n]范围内的随机整数的标准方法 - 或者只提供返回范围为[0,1)的float的random()函数的任何其他语言 - 是使用Math.floor(Math.random() * n)

现在假设我们在有理数的集合上运算,这背后的数学是微不足道的。问题是:由于IEEE-754浮点数的所有复杂性,得到的分布实际上是非常均匀的吗?

考虑到一个浮点数与下一个浮点数之间的差距随着它们变大而增加,我认为这应该会对较小的数字引入某种偏见。

5 个答案:

答案 0 :(得分:4)

不,对于n的大多数值,得到的分布不会完全一致。对于较小的值,它将非常接近均匀,以至于您很难检测到均匀分布的任何差异,但随着n变大,偏差会变得明显。

为了说明,这里有一些Python代码(不是J​​avaScript,对不起,但原理是一样的):

from collections import Counter
from random import random

def badrand(n):
    return int(random() * n)

print(Counter(badrand(6755399441055744) % 3 for _ in range(10000000)))

这会生成范围为[0, 6755399441055744)的1000万个随机整数,减少每个模3的整数,并计算余数为0,1或2的次数。如果我们生成这些整数一致地,我们期望模3的余数大致均匀分布,所以我们期望计数相似。

以下是在我的机器上运行此示例的结果:

Counter({1: 3751915, 0: 3334643, 2: 2913442})

也就是说,1的剩余部分显着0更容易发生,而2更可能发生random()random() }}。这里的差异是方式太大而无法通过随机变化来解释。

出了什么问题? Python的x / 2^53函数质量相对较高,基于Mersenne Twister,所以我们不太可能看到基本随机数生成器导致的统计问题。发生的事情是x生成2 ^ 53(大致)同样可能的结果之一 - 每个结果都是[0, 2^53)形式的badrand形式6755399441055744的范围badrand }。现在,在random()调用中,我们有效地将这些结果映射到random()可能的输出。现在这个价值没有随意选择(哈!);它恰好是2 ^ 53的3/4。这意味着在尽可能最均匀的分布下,2/3的可能Math.random()输出值正好被2 ^ 53个可能n输出值中的一个击中,而另外1/3是被2 ^ 53个n输出值中的两个命中。也就是说,一些潜在的输出两次可能与其他输出一样。所以我们距离制服还有很长的路要走。

您将在JavaScript中看到相同的效果。对于Chrome,n = 5显示为there are only 2^32 distinct results,因此您应该能够找到上述效果,5小于(但接近)2 ^ 32。< / p>

当然,同样的效果也适用于小2^32:如果2^32,那么因为Math.random()不是random()的除数,我们就无法完美在5个预期结果之间平均分配所有random()n结果:我们所希望的最佳结果是5个结果中的4个结果出现在每个可能的int(random() * n)结果的858993459中,而对于>Department(ID,Name) >Course(ID,Name,Dep_ID){Dep_ID foreign} >Student(ID,NAME,Dep_Id,Course_Id) {Dep_ID, Course_id Foreign Keys) 结果的858993460,第五次出现。但是,这种分布将非常接近统一,以至于几乎不可能找到任何统计测试来告诉你不同的东西。因此,出于实际目的,您应该使用小{{1}}安全。

http://bugs.python.org/issue9025可能会有一些相关的Python错误。通过摆脱计算这些数字的{{1}}方法,Python 3解决了这个问题。但是,Python 2中的错误仍然是remains

答案 1 :(得分:2)

如果Math.random(或等效物)从对应于[0,1]范围内的浮点数的那些位模式中产生均匀分布的位模式,则会产生极度偏置的样本。 [0.25,0.5]中的可表示浮点数与[0.5,1.0]中的数量一样多,这也是[0.125,0.25]中可表示值的相同数量。等等。简而言之,均匀分布的位模式将导致一千个值中只有一个在0.5和1.0之间。 (假设双精度浮点数。)

幸运的是,这不是Math.random所做的。获得均匀分布数(而不是位模式)的一种简单方法是在[1.0,2.0)中生成均匀分布的位模式,然后减去1.0;这是一个相当普遍的策略。

无论如何,由于量化偏差,Math.floor(Math.random() * n)的最终结果不是均匀分布,除非n是2的幂。可以由Math.random返回的可能浮点值的数量是2的幂,并且如果n不是2的幂,则不可能精确地均匀地分布可能的浮点值在[0,n)中的所有整数值。如果Math.random返回双精度浮动指针数并且n不大,则此偏差很小,但它确实存在。

答案 2 :(得分:0)

根据http://es5.github.io/x15.8.html#x15.8.2.14

Math.random的功能

  

返回带有正号的Number值,大于或等于0   但小于1,随机选择或伪随机选择近似   在该范围内均匀分布,使用   依赖于实现的算法或策略。这个功能不需要   参数。

看看这篇文章: https://stats.stackexchange.com/questions/40384/fake-uniform-random-numbers-more-evenly-distributed-than-true-uniform-data

这已经超出了我的头脑,对不起我没有什么可以贡献

答案 3 :(得分:0)

假设 random()返回0..1之间的数字。

如果结果是单个精度浮点数,那么基于尾数只有23位熵。

如果结果是双精度浮点数,那么基于尾数只有52位熵。

所以 floor(random()* N)只有在N小于2 ^ 24或2 ^ 53时才是均匀的。

编辑以下是有关浮点http://www.mathworks.com/help/matlab/ref/flintmax.html的最大连续整数的一些信息

答案 4 :(得分:0)

我认为你的评论“一个浮点数与下一个浮点数之间的差距随着它们变大而增加”基于以下内容:

在IEEE-754中你有一个固定大小的尾数,它允许[1,2]范围内的均匀“随机”值,并且在[2,4]中有相同数量的可能值,这是两倍如果范围很大,那么我们在可能的值之间得到2倍的间距,对于[4,8]等等也是两倍。

现在,我没有查看“..,使用依赖于实现的算法或策略”背后的技术细节,当他们谈论为[0,1)生成的随机数的属性时,但是因为以上考虑是如此微不足道,我确实假设随机发生器程序员已经意识到这一点并用“依赖于实现的算法......”来处理它。

因此,作为一个天真的家伙,我确实相信(我的假设)你的怀疑理由没有什么可担心的。事实上,我可能会认为,如果你可以为尾数生成统一和随机的值,那么设置总是相同的指数,使得值属于[1,2],你从所有东西中减去1并具有适当的分布对于[0,1)。