random.uniform(0,1) 可以生成 0 或 1 吗?

Can random.uniform(0,1) ever generate 0 or 1?

the documentation中据说uniform(0,1)有机会生成值01

我有 运行 uniform(0, 1) 10000 次,但它从未产生零。即使在 uniform(0, 0.001) 的情况下。

random.uniform(0,1) 能否生成 01

"Several times" 不够。 10,000 不够。 random.uniform 从 2^53 (9,007,199,254,740,992) 个不同的值中选择。您对其中 两个 感兴趣。因此,在获得恰好为 0 或 1 的值之前,您应该期望生成几个 quadrillion 个随机值。所以这是可能的,但很有可能您永远不会观察到它。

uniform(0, 1) 可以产生 0,但它 永远不会 产生 1.

documentation 告诉您端点 b 可以 包含在生成的值中:

The end-point value b may or may not be included in the range depending on floating-point rounding in the equation a + (b-a) * random().

所以对于 uniform(0, 1),公式 0 + (1-0) * random(),简化为 1 * random(),必须能够准确地产生 1。只有当 random.random() 正好是 1.0 时才会发生这种情况。但是,random() 永远不会 产生 1.0.

引用random.random() documentation

Return the next random floating point number in the range [0.0, 1.0).

符号[..., ...)表示第一个值是所有可能值的一部分,但第二个不是。 random.random() 最多会产生 非常接近 1.0 的值。 Python 的 float 类型是一个 IEEE 754 base64 floating point value,它编码了许多 二进制分数 (1/2、1/4、1/5 , 等) 组成的值,而 random.random() 产生的值只是从 2 ** -1 (1/2) 到 2 ** -53 中随机选择的 53 个这样的分数的总和(1/9007199254740992).

但是,由于它可以产生非常接近 1.0 的值,加上浮点数相乘时出现的舍入误差,您 可以 产生 b 对于 ab 一些 值。但是 01 不在这些值中。

请注意 random.random() 可以 产生 0.0,因此 a 始终包含在 random.uniform() 的可能值中(a + (b - a) * 0 == a).因为 random.random() 可以产生 2 ** 53 个不同的值(这 53 个二进制分数的所有可能组合),所以 2 ** 53 中只有 1(所以 9007199254740992 中有 1)个机会正在发生。

所以random.random()可以产生的最高可能值是1 - (2 ** -53);只需为 b - a 选择一个足够小的值,以便在乘以更高的 random.random() 值时进行舍入。 b - a 越小,发生这种情况的可能性就越大:

>>> import random, sys
>>> def find_b():
...     a, b = 0, sys.float_info.epsilon
...     while random.uniform(a, b) != b:
...         b /= 2
...     else:
...         return b
...
>>> print("uniform(0, {0}) == {0}".format(find_b()))
...
uniform(0, 4e-323) == 4e-323

如果你打b = 0.0,那么我们已经分了1023次了,上面的数值代表我们分了1019次就走运了。到目前为止我发现的最高值(运行 上述函数在 max() 的循环中)是 8.095e-320(1008 格),但可能还有更高的值。这完全是一场机会游戏。 :-)

如果 ab 之间的离散步骤不多,也会发生这种情况,例如当 ab 具有高指数时,因此可能会出现相距甚远。浮点值仍然只是近似值,它们可以编码的值的数量是有限的。例如,sys.float_info.maxsys.float_info.max - (2 ** 970) 之间只有 1 个二进制分数差异,因此 random.uniform(sys.float_info.max - (2 ** 970), sys.float_info.max) 有 50-50 的机会产生 sys.float_info.max:

>>> a, b = sys.float_info.max - (2 ** 970), sys.float_info.max
>>> values = [random.uniform(a, b) for _ in range(10000)]
>>> values.count(sys.float_info.max)  # should be roughly 5000
4997

当然可以。你已经在正确的轨道上尝试 uniform(0, 0.001) 了。只要继续限制界限,让它尽快发生。

>>> random.uniform(0., 5e-324)
5e-324
>>> random.uniform(0., 5e-324)
5e-324
>>> random.uniform(0., 5e-324)
0.0

您可以尝试生成一个循环来计算需要的迭代次数,以便显示为精确的 0(不要)。

此外,正如霍布斯所说,uniformly 采样的值数量为 9,007,199,254,740,992。这意味着看到 0 的概率恰好是 1/9,007,199,254,740,992。一般而言,四舍五入意味着您需要 平均 10 个四万亿样本才能找到 0。当然,您可能会在第一个样本中找到它10 次尝试,或者永远不会。

不可能对 1 进行采样,因为为值定义的区间用括号括起来,因此不包括 1。