【问题标题】:Python random.seed behaved strangelyPython random.seed 表现异常
【发布时间】:2017-10-04 13:00:00
【问题描述】:

我调用了 random.seed(234),然后调用了 random.randint(0, 99) 并收到了 92。当我再次重复此过程几次时,我收到了 86。当我第二次调用 random.randint 然后它返回92. 我希望第一个值是 86 而不是 92。为什么是 92?

完整的日志输出如下。我已经包含了所有这些内容,以防之前有一些动作可以解释看似错误的行为:

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]: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \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

【问题讨论】:

  • python 3.4 &gt;&gt;&gt; random.seed(234) &gt;&gt;&gt; random.randint(0, 99) 43 &gt;&gt;&gt; random.seed(234) &gt;&gt;&gt; random.randint(0, 99) 43 &gt;&gt;&gt; random.seed(234) &gt;&gt;&gt; random.randint(0, 99) 43。无法复制。
  • 用 python 2.7 测试,不同的值但每次都一样...
  • 是的,很奇怪。感谢@Jean-FrançoisFabre,我想这是一个边缘案例错误或记录但令人惊讶的行为。

标签: python random random-seed


【解决方案1】:

这里发生的情况是 IPython 系统中的某些东西正在使用 random 模块,因此从核心 Mersenne Twister 生成器提供的随机流中消耗数字。这意味着,如果您还使用random 模块,您只能看到流中不可预测的数字子集,因为 IPython 得到了其余部分。

我可以通过在调用random.randint 之间随机点击几次&lt;Enter&gt; 键来可靠地重现您看到的效果(在Python 2 和Python 3 上)(尽管实际上我使用random.random简单)。这是一个示例会话,在 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,嘿,马上!很多追溯。我不会完整地复制回溯(它们很长),但这是其中一个的结尾:

  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 库使用 random 模块来随机分配其任务(尽管根据 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

【讨论】:

  • 既有趣又可怕!并且分析得很好。
  • 如果你想从另一个发行版生成一些东西怎么办?说,正常吗?
  • @NewNameStat:我认为所有相同的推理都适用,因为random.gauss(例如)从与random.randint 相同的流中获取随机输入。所以规则再次是,如果您想要可重现的随机结果,请实例化并使用您自己的 Random 实例 my_random 并使用 my_random.gauss 代替 random.gauss
【解决方案2】:

我的 Python 2.7.5 无法重现相同的行为,但文档 (https://docs.python.org/2/library/random.html) 说

如果 a 不是 None 或 int 或 long,则使用 hash(a) 代替。 请注意,某些类型的哈希值在以下情况下是不确定的 PYTHONHASHSEED 已启用。

我会(只是意见)说它可能是由散列函数的不确定性行为引起的。您的 PYTHONHASHSEED 是否启用?

【讨论】:

  • 你怎么看?我做了'PYTHONHASHSEED' in os.environ 并得到了False
  • 这不应该在这里应用:PYTHONHASHSEED 不会影响整数散列的方式。
猜你喜欢
  • 1970-01-01
  • 2010-12-25
  • 1970-01-01
  • 1970-01-01
  • 2021-12-14
  • 2012-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多