簡體   English   中英

使用集合顯示非確定性行為的種子 Python RNG

[英]Seeded Python RNG showing non-deterministic behavior with sets

嘗試從集合中選擇偽隨機元素時,我看到了非確定性行為,即使 RNG 已播種(示例代碼如下所示)。 為什么會發生這種情況,我是否應該期望其他 Python 數據類型顯示類似的行為?

注意:我只在 Python 2.7 上測試過這個,但它可以在兩台不同的 Windows 計算機上重現。

類似問題: Python 隨機種子無法使用遺傳編程示例代碼的問題可能類似。 根據我的測試,我的假設是集合內運行到運行的內存分配差異導致為相同的 RNG 狀態選取不同的元素。

到目前為止,我還沒有在 Python 文檔中發現任何關於 set 或 random 的警告/問題的提及。

示例代碼(randTest 產生不同的輸出運行到運行):

import random

''' Class contains a large set of pseudo-random numbers. '''
class bigSet:
    def __init__(self):
        self.a = set()
        for n in range(2000):
            self.a.add(random.random())
        return


''' Main test function. '''
def randTest():
    ''' Seed the PRNG. '''
    random.seed(0)

    ''' Create sets of bigSet elements, presumably many memory allocations. ''' 
    b = set()
    for n in range (2000):
        b.add(bigSet())

    ''' Pick a random value from a random bigSet. Would have expected this to be deterministic. '''    
    c = random.sample(b,1)[0]
    print('randVal: ' + str(random.random()))           #This value is always the same
    print('setSample: ' + str(random.sample(c.a,1)[0])) #This value can change run-to-run
    return

OrderedSet是理想的選擇。

這里既不應該使用set也不應該使用frozenset ,因為沒有任何地方指定它們中的任何一個是有序的。 另一個答案有效的事實只是實施的一個意外。 集合是無序的,依賴於它們的順序會導致耦合到 Python 版本(可能還有機器)。

我得到的順序與Roland在 Python 3.8.6 中的答案不同(盡管兩次運行之間的順序恰好相同)。 盡管生成的隨機數是相同的。

要保留順序,從而保留基於random種子的random ,您必須使用有序數據結構,例如OrderedSet

如果您沒有可用的OrderedSet ,或者如果分析您的代碼顯示OrderedSet很慢,您可以使用OrderedDict並忽略其值。

如果你有 Python >= 3.6,那么由於性能優化,即使是常規的dict也會被排序。

我很確定你是對的,這個問題是由set的運行到運行內存分配差異引起的。 當我將您的程序更改為使用列表而不是集合時,我得到了確定性行為:

import random

''' Class contains a large list of pseudo-random numbers. '''
class bigList:
    def __init__(self):
        self.a = [random.random() for n in range(2000)]

''' Main test function. '''
def randTest():
    ''' Seed the PRNG. '''
    random.seed(0)

    ''' Create lists of bigList elements, presumably many memory allocations. '''
    b = [bigList() for n in range(2000)]

    ''' Pick a random value from a random bigSet. Would have expected this to be deterministic. '''
    c = random.sample(b, 1)[0]
    print('randVal: ' + str(random.random()))  # This value is always the same
    # and so is this now...
    print('setSample: ' + str(random.sample(c.a, 1)[0]))

randTest()

它與可變對象的對象實例化有關。 如果我創建了一set frozenset它確實會給出一個確定性的結果;

Python 2.7.11 (default, Jan  9 2016, 15:47:04) 
[GCC 4.2.1 Compatible FreeBSD Clang 3.4.1 (tags/RELEASE_34/dot1-final 208032)] on freebsd10
Type "help", "copyright", "credits" or "license" for more information.
>>> import random
>>> random.seed(0)
>>> set(frozenset(random.random() for i in range(5)) for j in range(5))
set([frozenset([0.7298317482601286, 0.3101475693193326, 0.8988382879679935, 0.47214271545271336, 0.6839839319154413]), frozenset([0.5833820394550312, 0.4765969541523558, 0.4049341374504143, 0.30331272607892745, 0.7837985890347726]), frozenset([0.7558042041572239, 0.5046868558173903, 0.9081128851953352, 0.28183784439970383, 0.6183689966753316]), frozenset([0.420571580830845, 0.25891675029296335, 0.7579544029403025, 0.8444218515250481, 0.5112747213686085]), frozenset([0.9097462559682401, 0.8102172359965896, 0.9021659504395827, 0.9827854760376531, 0.25050634136244054])])
>>> random.seed(0)
>>> set(frozenset(random.random() for i in range(5)) for j in range(5))
set([frozenset([0.7298317482601286, 0.3101475693193326, 0.8988382879679935, 0.47214271545271336, 0.6839839319154413]), frozenset([0.5833820394550312, 0.4765969541523558, 0.4049341374504143, 0.30331272607892745, 0.7837985890347726]), frozenset([0.7558042041572239, 0.5046868558173903, 0.9081128851953352, 0.28183784439970383, 0.6183689966753316]), frozenset([0.420571580830845, 0.25891675029296335, 0.7579544029403025, 0.8444218515250481, 0.5112747213686085]), frozenset([0.9097462559682401, 0.8102172359965896, 0.9021659504395827, 0.9827854760376531, 0.25050634136244054])])
>>> 

如果我沒記錯的話,CPython 使用(可變)對象的內存位置作為它的 id 和散列的鍵。

所以雖然對象的內容總是相同的,但它的 id 會有所不同;

In [13]: random.seed(0)

In [14]: k = set()

In [15]: for n in range (20):
    k.add(bigSet())
   ....:     

In [16]: for x in k:
    print(id(x))
   ....:     
34856629808
34856629864
34856631936
34856630424
34856629920
34856631992
34856630480
34856629976
34856632048
34856631040
34856630536
34856632104
34856630032
34856630592
34856630088
34856632160
34856629752
34856629696
34856630760
34856630256

In [17]: random.seed(0)

In [18]: k = set()

In [19]: for n in range (20):
   ....:         k.add(bigSet())
   ....:     

In [20]: for x in k:
   ....:         print(id(x))
   ....:     
34484534800
34856629808
34484534856
34856629864
34856631936
34856630424
34856629920
34856631992
34484534968
34856629976
34856630480
34856632048
34856631040
34484535024
34484535080
34484535136
34856632216
34484534688
34484534912
34484534744

一個可能的解決方案是將凍結集子類化。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM