2012-12-24 73 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