2016-03-30 8 views
3

RNGがシードされていても、セットから疑似ランダム要素を選択しようとすると、非決定論的な動作が発生します。なぜこれが起こっているのですか、ほかのPythonデータ型も同様の動作をすると思いますか?シード付きPython RNGセットで非確定的な振る舞いを示しています

注:これはPython 2.7でのみテストしましたが、2つの異なるWindowsコンピュータで再現可能です。

類似した問題:Python random seed not working with Genetic Programming example codeの問題は類似している可能性があります。私のテストに基づいて、私の仮説は、セット内のrun-to-runメモリ割り当ての違いが、同じRNG状態で取得されるさまざまな要素につながるということです。

これまでのところ、この種の警告/問題は、Pythonのdocsで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 
+1

Python 3.5の動作は同じです。 –

答えて

0

:私が代わりにセットのリストを使用するようにプログラムを変更したとき、私は確定的な動作を得ました。 setfrozensetにすると、決定的な結果が得られます。

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 
4 
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 

可能な解決策は、フリーズセットをサブクラス化することです。

+0

あなたのID(カスタムクラスのデフォルトの動作)ではなく、セットの内容に基づいて動作する '__eq__'メソッドと' __hash__'メソッドを 'bigSet'に追加することもできます。ハッシュ可能な型から継承することは、それらのメソッドを取得するための唯一の方法です。 – Blckknght

0

私はあなたが正しいならかなり確信している、と問題は実行ツーランメモリ割り当ての違いによって引き起こされます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() 
関連する問題