2

我正在尝试从 python 标准库中实现我自己版本的Random 类。我可以生成随机位,并实现了 getrandbits(n) 函数。但是超类不使用这个函数来计算从 random() 返回的浮点数。所以我必须自己实现:

def random(self):
    exp = 0x3FF0000000000000
    mant = self.getrandbits(52)
    return struct.unpack("d", struct.pack("q", exp^mant))[0]-1.0

我使用符号 0(正)、指数 1023(2^0 = 1)和随机尾数。所以我从 [1.0, 2.0) 中得到一个数字。random() 函数必须在 [0.0, 1.0) 中返回一个数字,所以我在返回之前减去 1.0。由于我不是浮点数方面的专家,因此我不确定这样做是否正确。我不会因为减法而失去精度吗?我可以从随机位构建数字,使其在 [0.0, 1.0) 中不减法吗?

4

1 回答 1

2

您的实现很好:假设它getrandbits本身足够随机,您的实现将以相等的概率生成表单的每个数字n / 2^520 <= n < 2^52因此它是 . 上均匀分布的一个很好的近似值[0, 1)。您使用的减法不是问题:减法的结果始终可以精确表示,因此减法中不存在舍入或精度损失。

Python 的random()实现沿着return self.getrandbits(53) / 2**53. 效果相似的路线做了更多的事情,只是输出的分布现在是原来的两倍:您以相等的概率获得表格n / 2^53的每个数字。0 <= n < 2^53大多数应用程序在实践中不太可能注意到这两种实现之间的差异。如果您关心速度,那么这也可能会更快,尽管您应该像往常一样进行分析以查明是否确实如此。

这些都不是完美的:在2^62range 中大约有不同的 IEEE 754 binary64 浮点数[0.0, 1.0),并且您的实现只能生成2^52不同的输出,因此上述任何一种实现都不能生成大多数浮点数。更好的实现可以生成范围内的random()每个浮点数,其概率等于该轮的子区间的长度到某种形式的最近舍入。然而,这样的实现会更加复杂(尽管实现起来并不特别困难),并且很少有应用程序会从更大的输出集中受益。正如 Python 之禅所说:“实用胜于纯洁。”x[0.0, 1.0][0.0, 1.0)x


编辑:为了说明上面的最后一段,这里有一些代码。该uniform函数用于根据上述描述getrandbits生成均匀分布的浮点数。[0, 1]

"""                                                                                                                                                                                                                                         
High quality uniform random variable on [0, 1].                                                                                                                                                                                             

Simulates round(X) where X is a real random variable uniformly distributed on                                                                                                                                                               
the interval [0, 1] and round is the usual round-to-nearest rounding function                                                                                                                                                               
from real numbers to floating-point.                                                                                                                                                                                                        

"""
from __future__ import division
import random

precision = 53
emin = -1021

def random_significand():
    return (random.getrandbits(precision) + 1) // 2 / (2**precision)

def uniform():
    for i in xrange(1 - emin):
        if random.getrandbits(1):
            return (random_significand() + 0.5) / 2**i
    # Subnormal                                                                                                                                                                                                                             
    return random_significand() / 2**i
于 2012-12-24T17:32:44.313 回答