2017-10-04 5 views
4

私はrandom.seed(234)を呼び出し、random.randint(0,99)という名前で92を受け取りました。このプロセスを何度か繰り返して86回受け取りました。random.randint 2回目は92に戻ります。最初の値は92ではなく86になると考えていました。なぜ92ですか?Python random.seedは変な振る舞いをしました

完全なログ出力は以下のとおりです。

In [1]: import random 

In [2]: import string 

In [3]: string.letters 
Out[3]: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' 

In [4]: string.ascii_letters 
Out[4]: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 

In [5]: string.printable 
Out[5]: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>[email protected][\\]^_`{|}~ \t\n\r\x0b\x0c' 

In [6]: len(string.printable) 
Out[6]: 100 

In [7]: [string.printable[random.randint(0,99)] for i in range(20)] 
Out[7]: 
['{', 
'+', 
'[', 
'\r', 
'R', 
'Z', 
'v', 
'|', 
'v', 
'e', 
'T', 
'x', 
'\\', 
'}', 
'0', 
'>', 
'V', 
'\n', 
'`', 
'`'] 

In [8]: ''.join([string.printable[random.randint(0,99)] for i in range(20)]) 
Out[8]: '%Z\\%mx4Z53uUZIa5KHe*' 

In [9]: ''.join([string.printable[random.randint(0,99)] for i in range(20)]) 
Out[9]: 'Fg\nDHW+oV?-9``}\x0by%xD' 

In [10]: import os 

In [11]: os.urandom(1) 
Out[11]: '(' 

In [12]: os.urandom(1) 
Out[12]: '8' 

In [13]: os.urandom(1) 
Out[13]: '\xb1' 

In [14]: os.urandom(1) 
Out[14]: ')' 

In [15]: os.urandom(1) 
Out[15]: '\x8c' 

In [16]: os.urandom(1) 
Out[16]: '^' 

In [17]: os.urandom(1) 
Out[17]: '{' 

In [18]: os.urandom(1) 
Out[18]: '\x8f' 

In [19]: ''.join(os.urandom(10)) 
Out[19]: '{t\x8dR\x1d\x83\xef\xd6N\xbd' 

In [20]: ''.join(os.urandom(10)) 
Out[20]: '\x96\\\xf6\xe3\xf4/\x1f\xc7\x90\x02' 

In [21]: from random import SystemRandom 

In [22]: crypt = SystemRandom() 

In [23]: ''.join([string.printable[crypt.randrange(100)] for i in range(20)]) 
Out[23]: "WoDVH\r1!?1+djB'f<;nW" 

In [24]: ''.join([string.printable[crypt.randrange(100)] for i in range(20)]) 
Out[24]: '\rf?zo`7^{Y_Zx^[SYw7c' 

In [25]: ''.join([string.printable[crypt.randrange(100)] for i in range(20)]) 
Out[25]: "3k*uGVIP'~^{P*~bserk" 

In [26]: ''.join([string.printable[crypt.randrange(100)] for i in range(20)]) 
Out[26]: '~lkM/a&#_F&D\n<sC&i\r\n' 

In [27]: random.seed(234) 

In [28]: random.randint(0,99) 
Out[28]: 92 

In [29]: random.seed(234) 

In [30]: random.randint(0,99) 
Out[30]: 86 

In [31]: random.seed(234) 

In [32]: random.randint(0,99) 
Out[32]: 86 

In [33]: random.seed(234) 

In [34]: random.randint(0,99) 
Out[34]: 86 

In [35]: random.randint(0,99) 
Out[35]: 92 

In [36]: random.randint(0,99) 
Out[36]: 48 

In [37]: random.seed(234) 

In [38]: random.randint(0,99) 
Out[38]: 86 

In [39]: import sys 

In [40]: sys.version_info 
Out[40]: sys.version_info(major=2, minor=7, micro=13, releaselevel='final', serial=0) 

In [41]: sys.version 
Out[41]: '2.7.13 (default, Dec 17 2016, 23:03:43) \n[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)]' 

** 編集、奇妙:一見バギーの動作を説明することができ、いくつかの前のアクションがあった包み私はそれのすべてを含めました「同じ」一見バギー動作は同じ端末で ** を繰り返しウィンドウ前のipythonセッションを閉じました。私はいくつかのコマンドラインアクティビティを行い、ipythonを再び開いた。私は別の仕事をしました。その後、私は再びこれを試してみました:

In [37]: import random 

In [38]: random.seed(234) 

In [39]: random.randint(0, 99) 
Out[39]: 85 

In [40]: random.randint(0, 99) 
Out[40]: 50 

In [41]: random.seed(234) 

In [42]: random.randint(0, 99) 
Out[42]: 86 

In [43]: random.randint(0, 99) 
Out[43]: 92 
+1

のpython 3.4 '>>>ランダム。シード(234) >>> random.randint(0、99) 43 >>> random.seed(234) >>> random.randint(0、99) 43 >>> random.seed( 234) >>> random.randint(0,99) 43 '。再現できません。 –

+0

python 2.7でテストされていますが、毎回同じ値ではありますが、同じ値です... –

+0

ええ、変わってるよ。ありがとう@ Jean-FrançoisFabre、私はこれが端の場合のバグか文書化されている驚くべき振る舞いだと思います。 – AJP

答えて

7

ここで起こっていることは、IPythonシステムの何かがrandomモジュールを利用しており、コアのMersenne Twisterジェネレータが提供するランダムストリームから数値を消費していることです。つまり、randomモジュールを使用している場合、IPythonは残りの部分を取得するので、ストリームから予測できない部分集合のストリームしか表示されません。

(実際に私は簡単にするためrandom.randomを使用していますが)、私はあなたがrandom.randintの呼び出しの間でランダムに<Enter>キーを数回押すことで(Pythonの2とPython 3の両方で)確実に見ている効果を再現することができます。ここでは、macOS 10.12.6でのPython 3.6.2とIPython 6.2.0のセッション例を示します。

In [1]: import random 

In [2]: random.seed(234) 

In [3]: 

In [3]: 

In [3]: random.random() 
Out[3]: 0.8579160018299248 

In [4]: random.random() 
Out[4]: 0.5055065431394443 

In [5]: random.seed(234) 

In [6]: random.random() 
Out[6]: 0.26476014305349627 

In [7]: random.random() 
Out[7]: 0.8579160018299248 

In [8]: random.random() 
Out[8]: 0.5055065431394443 

私の仮説を確認するには、私はRandomクラスに次のメソッドを追加することにより、標準ライブラリのrandom.pyファイルにRandom.random方法をオーバーライドしてハッキング:

def random(self): 
    print("random being called") 
    import traceback; traceback.print_stack() 
    return super(Random, self).random() 

は今IPythonを起動します、そして、ちょっとプレスト!トレースバックがたくさんあります。私は(彼らは長いよ)に完全にトレースバックを再現しませんが、ここではそれらの1のテールエンドです:

File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/IPython/terminal/interactiveshell.py", line 376, in prompt_for_code 
    pre_run=self.pre_prompt, reset_current_buffer=True) 
    File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/prompt_toolkit/interface.py", line 415, in run 
    self.eventloop.run(self.input, self.create_eventloop_callbacks()) 
    File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/prompt_toolkit/eventloop/posix.py", line 157, in run 
    random.shuffle(tasks) 
    File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/random.py", line 278, in shuffle 
    j = randbelow(i+1) 
    File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/random.py", line 250, in _randbelow 
    r = random() 
    File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/random.py", line 98, in random 
    import traceback; traceback.print_stack() 

あなたが見ることができるように、IPythonで使用されているprompt_toolkitライブラリは、使用しています(この変更は最近削除されたが、CHANGELOGによる)。あなたは確実に再現可能な乱数ストリームが必要な場合は

、明示的なrandom.Randomインスタンスを作成し、それを使用:

In [1]: from random import Random 

In [2]: my_random = Random() 

In [3]: my_random.seed(234) 

In [4]: my_random.randint(0, 99) 
Out[4]: 43 

In [5]: my_random.randint(0, 99) 
Out[5]: 33 

In [6]: my_random.seed(234) 

In [7]: my_random.randint(0, 99) 
Out[7]: 43 

In [8]: my_random.randint(0, 99) 
Out[8]: 33 
+0

同時に面白いと恐ろしい!そして素晴らしい分析。 – sascha

-1

私はPython 2.7.5は、同じ動作を再現することはできませんが、いずれかのintまたはlongでない場合ドキュメント (https://docs.python.org/2/library/random.htmlは)

を語ります代わりにhash(a)が使用されます。 PYTHONHASHSEEDが有効な場合、一部のタイプのハッシュ値が非決定的であることに注意してください。

これは、ハッシュ関数の非決定論的な振る舞いによって引き起こされると考えられます。あなたのPYTHONHASHSEEDは有効になっていますか?

+0

どのように伝えますか?私はos.environ'で 'PYTHONHASHSEED 'をして' False'を得ました。 – AJP

+0

これはここでは当てはまりません: 'PYTHONHASHSEED'は整数ハッシュの方法には影響しません。 –

関連する問題