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

时间:2015-06-06 13:59:43

标签: python floating-point floating-point-precision

我正在尝试使用random.uniform而不是random.getrandbits生成0或1,有50/50的几率。

这就是我所拥有的

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。但我不知道如何使用它来解决这个问题。

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

2 个答案:

答案 0 :(得分:3)

  

我知道Python有一个实习最小浮点值,在sys.float_info.min中显示,在我的电脑中是2.22507385851e-308。但我不知道如何使用它来解决这个问题。

2.22507385851e-308不是最小的正浮点值,它是最小的正标准化浮点值。最小的正浮点值是2 -52 倍,即接近5e-324。

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

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

答案 1 :(得分:2)

根据sourcerandom.uniform实施{<1}}:

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的数字时会给出0.5,而当乘以大于{{的数字时会产生另一个结果1}}。我发现,在我的机器上,该号码为0.5

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

5e-324

返回的结果与50%概率一致:

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)