使用近似生成均匀分布的位

Generating evenly distributed bits, using approximation

我正在尝试使用 random.uniform 而不是 random.getrandbits.

以 50/50 的概率生成 0 或 1

这是我的

0 if random.uniform(0, 1e-323) == 0.0 else 1

但如果我 运行 时间足够长,则生成 1 的平均值约为 70%。如下所示:

sum(0 if random.uniform(0, 1e-323) == 0.0 
    else 1 
    for _ in xrange(1000)) / 1000.0  # --> 0.737

如果我将其更改为 1e-324,它将始终为 0。如果我将其更改为 1e-322,则平均值将为 ~%90。

我做了一个肮脏的程序,试图通过除法和乘法多次找到 1e-322 和 1e-324 之间的最佳点:

v = 1e-323
n_runs = 100000
target = n_runs/2

result = 0
while True:
    result = sum(0 if random.uniform(0, v) == 0.0 else 1 for _ in xrange(n_runs))

    if result > target:
        v /= 1.5
    elif result < target:
        v *= 1.5 / 1.4
    else:
        break

print v

最后是 4.94065645841e-324

但是如果我运行多次还是会出错

没有我写的脏脚本,我有办法找到这个号码吗?我知道 Python 有一个内部最小浮点值,显示在 sys.float_info.min 中,在我的电脑中是 2.22507385851e-308。但是我不知道如何使用它来解决这个问题。

抱歉,如果这感觉更像是一个谜题而不是一个正确的问题,但我自己无法回答。

I know that Python has a intern min float value, show in sys.float_info.min, which in my PC is 2.22507385851e-308. But I don't see how to use it to solve this problem.

2.22507385851e-308 不是最小的正浮点值,它是最小的正 normalized 浮点值。最小的正浮点值是2-52倍,也就是接近5e-324.

2-52 称为“机器 epsilon”,通常将浮点类型的“min”称为最小值所有可比值中的(即 -inf),也不是有限值中的最小值(即 -max),也不是正值中的最小值。

那么,你面临的下一个问题就是random.uniform不统一到那个级别。当你传递一个标准化数字时它可能工作正常,但如果你传递给它最小的正可表示浮点数,它在内部使用它进行的计算可能非常近似并导致它的行为与文档中所说的不同。尽管根据您的“脏脚本”的结果,它似乎工作正常。

根据 source:

,这是 random.uniform 的实现
from os import urandom as _urandom

BPF = 53        # Number of bits in a float
RECIP_BPF = 2**-BPF

def uniform(self, a, b):
    "Get a random number in the range [a, b) or [a, b] depending on rounding."
     return a + (b-a) * self.random()

def random(self):
     """Get the next random number in the range [0.0, 1.0)."""
     return (int.from_bytes(_urandom(7), 'big') >> 3) * RECIP_BPF

因此,您的问题归结为找到一个数字 b,当乘以小于 0.5 的数字时会得到 0,当乘以大于 0.5 的数字时会得到另一个结果0.5。我发现,在我的机器上,这个数字是 5e-324.

为了测试它,我制作了以下脚本:

from random import uniform

def test():
    runs = 1000000
    results = [0, 0]
    for i in range(runs):
        if uniform(0, 5e-324) == 0:
            results[0] += 1
        else:
            results[1] += 1
    print(results)

哪个返回的结果符合 50% 的概率:

>>> test()
[499982, 500018]
>>> test()
[499528, 500472]
>>> test()
[500307, 499693]