2012-12-24 41 views
2

我试图从python标准库实现我自己的Random class版本。我可以生成随机位,并执行getrandbits(n)函数。但超类不使用此函数来计算random()返回的浮点数。所以,我有我自己实现一个:在Python中为Random.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)指数和随机mantisse。所以我从[1.0,2.0]得到一个数字。 random()函数必须在[0.0,1.0)中返回一个数字,所以我在返回之前减去1.0。 由于我对浮点数不是专家,我不确定这是否以正确的方式完成。我不减去精度吗?我可以从随机位中构建数字,以便它在[0.0,1.0)中不加减?

+0

对不起,但这听起来对我来说是一个非常糟糕的主意。问自己:“唐纳德克努特会做什么?”并对最终解决方案进行Diehard测试。 – duffymo

+2

这实际上并不是关于随机生成器。这可以认为是正确的(因为我正在围绕现有的和经过测试的生成器封装Python类)。问题是,如何根据随机位的python标准库来组装浮点随机数。 – qasfux

回答

2

你的实现是好的:假设getrandbits本身就足以随机的,您的实现将产生形式n/2^52的每个数字为0 <= n < 2^52以相等的概率,所以这是一个很好的近似上[0, 1)均匀分布。你使用的减法不是一个问题:减法的结果总是可以精确表示的,所以在减法中没有舍入或精度损失。

Python的random()实现不沿return self.getrandbits(53)/2**53.线的影响是类似的东西多,但产出的分布是现在的两倍罚款:你得到的形式n/2^53的每个数0 <= n < 2^53概率相同。大多数应用程序在实践中不太可能会注意到这两种实现之间的差异。如果你关心速度,这很可能也会更快,尽管你总是应该知道这是否是事实。

这些都不是完美的:有大约2^62的IEEE 754 binary64浮动范围[0.0, 1.0),而您的实现只能产生2^52不同输出,所以大部分这些彩车不能由上述任何一种实现的产生。更好的random()实现可以产生范围为[0.0, 1.0]的每个浮点数x,其概率等于子周期的长度[0.0, 1.0),该概率在某种形式的舍入到最近的情况下舍入到x。然而,这样的实现将会复杂得多(尽管不是特别难以实现),并且很少的应用程序会从更大的输出集中受益。正如Python的禅说:“实用性胜过纯洁。”


编辑:为了说明的最后一段之上,这里的一些代码。根据上面的描述,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