2017-10-19 55 views
1

我想用基於散列的數據生成器(以Python)替換現有的基於隨機數的數據生成器,以便它不再需要按順序生成所有內容,如this article的啓發。我應該如何從基於PRNG的生成轉移到基於散列的程序生成?

我可以通過取整數版本的哈希值並將其除以哈希值的最大值來創建一個從0到1的浮點數。

我可以創建一個平坦的整數範圍,將浮點數乘以平坦範圍。我大概可以使用模和生活的偏見,因爲散列範圍很大,我的範圍很小。

我怎樣才能使用哈希來創建一個高斯或正態分佈式浮點值?

對於所有這些情況,我最好是使用我的散列作爲一個新的random.Random對象的種子,並使用該類中的函數來生成我的數字並依靠它們來獲得分配特性?

目前,我的代碼的結構是這樣的:

num_people = randint(1,100) 
people = [dict() for x in range(num_people)] 
for person in people: 
    person['surname'] = choice(surname_list) 
    person['forename'] = choice(forename_list) 

的問題是,對於一個給定的種子是一致的,我一定要產生相同的順序所有的人,我不得不生成姓氏,然後生成姓氏。如果我在兩者之間添加一箇中間名,那麼生成的名字將會改變,所有後續人的所有名字也會改變。

我想構建這樣的代碼:

h1_groupseed=1 

h2_peoplecount=1 
h2_people=2 

h4_surname=1 
h4_forename=2 

num_people = pghash([h1_groupseed,h2_peoplecount]).hashint(1,100) 
people = [dict() for x in range(num_people)] 
for h3_index, person in enumerate(people,1): 
    person['surname'] = surname_list[pghash([h1_groupseed,h2_people,h3_index,h4_surname]).hashint(0, num_of_surnames - 1)] 
    person['forename'] = forename_list[pghash([h1_groupseed,h2_people,h3_index,h4_forename]).hashint(0, num_of_forenames - 1)] 

這將使用傳遞給pghash生成哈希值,並使用該散列以某種方式建立的僞隨機結果。

+0

你爲什麼要這麼做? –

+0

您可以使用Box Muller轉換將均勻分佈的變量更改爲普通變量。 https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform – WNG

+0

@ReblochonMasque因爲我想讓數據生成器對屬性生成順序的更改具有魯棒性。 – PhilHibbs

回答

1

首先,一個很大的警告:不要捲起自己的CRYPTO。 如果您爲了安全目的而嘗試這樣做,請不要。

接下來,看看這個問題,其中列出了幾種方法可以做到你想要什麼,即改變一個隨機變量統一到一個正常的: Converting a Uniform Distribution to a Normal Distribution

+0

我編輯了我的問題,使其與正常分佈問題不同。 – PhilHibbs

1

除非你這樣做是爲自己的娛樂或作爲學習練習,我很強烈的建議是不要這樣做

PRNGs具有相同的總體結構,即使細節是非常不同的。它們的種子值小號映射到經由一些函數f的初始狀態小號小號← F(小號);然後它們通過一些變換ħ迭代狀態:小號 i + 1的← H(小號);最後他們經由一些函數g的狀態映射到輸出ûù←克(小號)。 (對於簡單的PRNG,f()或g()通常是標識函數。對於更復雜的發電機,如Mersenne Twister,涉及更多。)

狀態轉換函數h()用於在狀態空間中均勻分佈新狀態。換句話說,它已經是一個哈希函數,但是對於任何廣泛接受的生成器而言,它已經被專家嚴格審查以獲得良好的統計行爲。

Mersenne Twister,Python的默認PRNG,已經在數學上證明有k元組共同均勻地分佈在所有k中。我猜測你選擇的任何散列函數都不能做出這樣的聲明。另外,崩潰函數g()應該保持結果的一致性。你已經建議你「可以使用整數版本的散列來創建一個平坦的數字範圍,只需通過取模。」一般來說,這將引入modulo bias,所以你不會最終得到均勻分佈的結果。

如果你堅持使用內置的PRNG,沒有理由不使用內置的高斯發生器。如果你想爲自己的娛樂而做,那麼有很多資源可以告訴你如何將制服映射到高斯。衆所周知的方法包括Box-Muller方法,Marsaglia's polar methodziggurat方法。


UPDATE

鑑於你在你的問題中提供的其他信息,我覺得你想要的答案被包含在Python的文件本節random:提供

功能通過這個模塊實際上是random.Random類的隱藏實例的綁定方法。你可以實例化你自己的Random實例來獲取不共享狀態的生成器。這個 對多線程程序特別有用,爲每個線程創建一個不同的Random實例,並使用jumpahead()方法 使每個線程看到的生成序列可能不重疊。

聽起來像是要用於每個personRandom單獨實例,接種彼此獨立地或與作爲random.jumpahead()文檔中所述同步,但廣泛分離的狀態。這是仿真建模者自1950年代早期以來使用的方法之一,因此它們可以保持配置之間的可重複性,以便以公平的方式直接比較兩個或更多系統。查看this article第二頁上的「同步」的討論,或者從this book chapter的第8頁開始,或者拿起大多數大學圖書館提供的幾十種模擬教科書,並閱讀「常見隨機數字」部分。 (我不指着你對維基百科,因爲它提供了關於這個話題幾乎沒有細節。)

這裏的示出創建的Random多個實例明確的例子:

import random as rnd 

print("two PRNG instances with identical seeding produce identical results:") 
r1 = rnd.Random(12345) 
r2 = rnd.Random(12345) 
for _ in range(5): 
    print([r1.normalvariate(0, 1), r2.normalvariate(0, 1)]) 

print("\ndifferent seeding yields distinct but reproducible results:") 
r1 = rnd.Random(12345) 
r2 = rnd.Random(67890) 
for _ in range(3): 
    print([r1.normalvariate(0, 1), r2.normalvariate(0, 1)]) 
print("\nresetting, different order of operations") 
r1 = rnd.Random(12345) 
r2 = rnd.Random(67890) 
print("r1: ", [r1.normalvariate(0, 1) for _ in range(3)]) 
print("r2: ", [r2.normalvariate(0, 1) for _ in range(3)]) 
+0

所以我應該使用內置的隨機模塊,但每次使用散列作爲新鮮的種子?這就說得通了。我希望每次構建一個新的Random實例的代價不是太大。 – PhilHibbs

+0

@PhilHibbs不!良好的分配屬性來自PRNG內置的h()和g()轉換,而不是播種。播種費用對於Mersenne Twister來說是非常昂貴的,反覆重複的操作實際上可能會損害PRNG設計人員竭力爲您提供的分配屬性。 (做一個搜索所有的「爲什麼隨機繼續給予相同的價值」類型的帖子在SO上。)不要重新編入,除非你真的知道你在做什麼,並有一個很好的理由這樣做。 – pjs

+0

我的[「很好的理由」](https://blogs.unity3d.com/2015/01/07/a-primer-on-repeatable-random-numbers/)是我不想生成所有每次數據的順序完全一樣。看看我剛剛添加的示例代碼 - 如何添加「中間名」屬性,而沒有隨後的所有人爲給定的隨機種子命名完全不同? – PhilHibbs

0

我已經取得了進展,並創建了一個簡單基於散列的替代一些在random.Random類的功能:

from __future__ import division 
import xxhash 
from numpy import sqrt, log, sin, cos, pi 

def gaussian(u1, u2): 
    z1 = sqrt(-2*log(u1))*cos(2*pi*u2) 
    z2 = sqrt(-2*log(u1))*sin(2*pi*u2) 
    return z1,z2 

class pghash: 
    def __init__(self, tuple, seed=0, sep=','): 
     self.hex = xxhash.xxh64(sep.join(tuple), seed=seed).hexdigest() 

    def pgvalue(self): 
     return int(self.hex, 16) 

    def pghalves(self): 
     return self.hex[:8], self.hex[8:] 

    def pgvalues(self): 
     return int(self.hex[:8], 16), int(self.hex[8:], 16) 

    def random(self): 
     return self.value()/2**64 

    def randint(self, min, max): 
     return int(self.random() * max + min) 

    def gauss(self, mu, sigma): 
     xx = self.pgvalues() 
     uu = [xx[0]/2**32, xx[1]/2**32] 
     return gaussian(uu[0],uu[1])[0] 

下一步是要經過我的代碼,並取代所有random.Random方法與pghash對象的調用。

我已經制作成一個模塊,我希望上傳在某一點的PyPI此: https://github.com/UKHomeOffice/python-pghash