不,对于n 的大多数值,生成的分布不会完全一致。对于较小的值,它将非常接近均匀分布,以至于您很难检测到与均匀分布的任何差异,但随着 n 变大,偏差会变得明显。
为了说明,这里有一些 Python 代码(不是 JavaScript,对不起,但原理是一样的):
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 更可能发生,而0 的剩余部分又比2 的剩余部分更可能发生。这里的差异方式太大了,无法用随机变化来解释。
所以出了什么问题? Python 的random() 函数质量相对较高,基于Mersenne Twister,因此我们不太可能看到由基本随机数生成器导致的统计问题。正在发生的事情是random() 生成 2^53 个(大致)同样可能的结果之一 - 每个结果都是x / 2^53 形式的数字,用于x 范围内的某个整数[0, 2^53)。现在在badrand 调用中,我们有效地将这些结果映射到6755399441055744 可能的输出。现在该值不是随机选择的(哈!);它正好是 2^53 的 3/4。这意味着在可能的最均匀分布下,可能的 badrand 输出值的 2/3 恰好被 2^53 个可能的 random() 输出值之一命中,而其他 1/3 被 两个 2^53 个可能的random() 输出值。也就是说,某些潜在输出发生的可能性是其他输出的 两倍。所以我们离制服还有很长的路要走。
您将在 JavaScript 中看到相同的效果。在 Chrome 的情况下,there are only 2^32 distinct results 似乎来自Math.random(),因此您应该能够找到类似上述的效果,n 小于(但接近)2^32。
当然,同样的效果也适用于小的n:如果n = 5,那么因为5 不是2^32 的除数,我们不可能完美地均匀分布所有2^32 可能的@ 5 个期望结果之间的 987654350@ 结果:我们可以希望的最好结果是,5 个结果中的 4 个出现在可能的 random() 结果中的 858993459 个中,而第五个出现在 random() 结果中的 858993460 个中。但是这种分布将非常接近均匀,几乎不可能找到任何统计测试来告诉你不同的情况。因此,出于实际目的,您应该使用小的n 安全。
http://bugs.python.org/issue9025 上可能有一个相关的 Python 错误。通过摆脱计算这些数字的int(random() * n) 方法,Python 3 解决了该错误。不过,Python 2 中的错误仍然是 remains。