首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Python 2和3与给定种子洗牌的区别

Python 2和3与给定种子洗牌的区别
EN

Stack Overflow用户
提问于 2016-08-14 14:09:41
回答 3查看 2.1K关注 0票数 10

我正在编写一个与Python2.7和3.5兼容的程序。它的某些部分依赖随机过程。我的单元测试使用了任意的种子,这导致了跨执行和语言的相同结果..。除了使用random.shuffle的代码。

Python 2.7中的示例:

代码语言:javascript
运行
复制
In[]:   import random
        random.seed(42)
        print(random.random())
        l = list(range(20))
        random.shuffle(l)
        print(l)
Out[]:  0.639426798458
        [6, 8, 9, 15, 7, 3, 17, 14, 11, 16, 2, 19, 18, 1, 13, 10, 12, 4, 5, 0]

Python 3.5中的相同输入:

代码语言:javascript
运行
复制
In []:  import random
        random.seed(42)
        print(random.random())
        l = list(range(20))
        random.shuffle(l)
        print(l)
Out[]:  0.6394267984578837
        [3, 5, 2, 15, 9, 12, 16, 19, 6, 13, 18, 14, 10, 1, 11, 4, 17, 7, 8, 0]

请注意,伪随机数是相同的,但洗牌列表是不同的。正如预期的那样,重新执行单元格不会更改它们各自的输出。

如何为Python的两个版本编写相同的测试代码?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2016-08-14 14:31:20

在Python3.2中,对随机模块进行了一些重构,以使输出跨体系结构(给定相同的种子)一致,请参见第7889期shuffle()方法被转换为使用Random._randbelow()

但是,_randbelow()方法也进行了调整,因此仅仅复制3.5版的shuffle()并不足以解决这个问题。

尽管如此,如果您传递自己的random()函数,Python3.5中的实现将与2.7版本保持不变,因此可以绕过这一限制:

代码语言:javascript
运行
复制
random.shuffle(l, random.random)

但是请注意,与现在相比,您受到了#7889试图解决的旧的32位对64位架构差异的影响。

忽略几个优化和特殊情况,如果包含_randbelow(),3.5版本可以支持如下:

代码语言:javascript
运行
复制
import random
import sys

if sys.version_info >= (3, 2):
    newshuffle = random.shuffle
else:
    try:
        xrange
    except NameError:
        xrange = range

    def newshuffle(x):
        def _randbelow(n):
            "Return a random int in the range [0,n).  Raises ValueError if n==0."
            getrandbits = random.getrandbits
            k = n.bit_length()  # don't use (n-1) here because n can be 1
            r = getrandbits(k)          # 0 <= r < 2**k
            while r >= n:
                r = getrandbits(k)
            return r

        for i in xrange(len(x) - 1, 0, -1):
            # pick an element in x[:i+1] with which to exchange x[i]
            j = _randbelow(i+1)
            x[i], x[j] = x[j], x[i]

这使您在2.7上的输出与3.5相同:

代码语言:javascript
运行
复制
>>> random.seed(42)
>>> print(random.random())
0.639426798458
>>> l = list(range(20))
>>> newshuffle(l)
>>> print(l)
[3, 5, 2, 15, 9, 12, 16, 19, 6, 13, 18, 14, 10, 1, 11, 4, 17, 7, 8, 0]
票数 16
EN

Stack Overflow用户

发布于 2016-08-14 16:34:19

详细阐述Martijn的出色回答和评论,在这个讨论上,我终于找到了一个解决办法,这个解决方案可以说没有回答我的问题,但同时也不需要进行深刻的更改。总括而言:

  • random.seed实际上使每个random函数都是确定性的,但不一定在不同版本之间产生相同的输出;
  • PYTHONHASHSEED设置为0将禁用字典和集合的哈希随机化,默认情况下,这会在Python3中引入一个不确定因素。

因此,在启动Python 3测试的bash脚本中,我添加了:

代码语言:javascript
运行
复制
export PYTHONHASHSEED=0

然后,我暂时更改了我的测试函数,以便强制使用整数种子,它将在Python 3中复制Python 2中预期的结果。最后,我返回更改并替换了行:

代码语言:javascript
运行
复制
seed(42)

就像这样:

代码语言:javascript
运行
复制
seed(42 if sys.version_info.major == 2 else 299)

没有什么值得吹嘘的,但正如俗语所说,有时实用性胜过纯洁;)

这个快速的解决方案对于想要在不同版本的Python上测试相同的随机代码的人可能很有用!

票数 1
EN

Stack Overflow用户

发布于 2019-01-22 13:26:58

如果我错了,可能有人会纠正我,但numpy.random模块似乎不会在python2和3之间更改。

代码语言:javascript
运行
复制
>>> import numpy as np
>>> l = list(range(20))
>>> np.random.RandomState(42).shuffle(l)
>>> l
[0, 17, 15, 1, 8, 5, 11, 3, 18, 16, 13, 2, 9, 19, 4, 12, 7, 10, 14, 6]

我在Python2.7(np1.12.1)和3.7 (np1.14.5)中得到了相同的结果。

文档还声明生成了数字版本之间应该是相同的。

兼容性保证使用相同参数的固定种子和对“RandomState”方法的一系列固定调用将始终产生相同的结果,直到舍入错误,除非值不正确。将修复不正确的值,并在相关的docstring中注明进行修复的NumPy版本。只要以前的行为保持不变,就允许扩展现有的参数范围和添加新的参数。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38943038

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档