Python 2 和 3 之间的差异,用于使用给定种子进行洗牌 [英] Difference between Python 2 and 3 for shuffle with a given seed

查看:57
本文介绍了Python 2 和 3 之间的差异,用于使用给定种子进行洗牌的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Python 2.7 中的示例:

In[]:随机导入随机种子(42)打印(随机.随机())l = 列表(范围(20))random.shuffle(l)打印(升)出[]: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 中的相同输入:

在[]中:随机导入随机种子(42)打印(随机.随机())l = 列表(范围(20))random.shuffle(l)打印(升)出[]:0.6394267984578837[3, 5, 2, 15, 9, 12, 16, 19, 6, 13, 18, 14, 10, 1, 11, 4, 17, 7, 8, 0]

注意伪随机数是一样的,只是shuffle后的list不同.正如预期的那样,重新执行单元不会改变它们各自的输出.

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

解决方案

在 Python 3.2 中,对 random 模块进行了一些重构,以使跨架构的输出统一(给定相同的种子),请参阅 问题 #7889.shuffle() 方法切换为使用 Random._randbelow().

然而,_randbelow() 方法进行了调整,所以简单地复制 3.5 版本的 shuffle() 是不够的这个.

也就是说,如果您传入自己的 random() 函数,Python 3.5 中的实现与 2.7 版本相比没有变化,因此可以让您绕过此限制:

random.shuffle(l, random.random)

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

忽略一些优化和特殊情况,如果你包含 _randbelow() 3.5 版本可以反向移植为:

随机导入导入系统如果 sys.version_info >= (3, 2):newshuffle = random.shuffle别的:尝试:范围除了名称错误:xrange = 范围def newshuffle(x):def_randbelow(n):返回 [0,n 范围内的随机整数.如果 n==0,则引发 ValueError."getrandbits = random.getrandbitsk = n.bit_length() # 这里不要使用 (n-1) 因为 n 可以是 1r = getrandbits(k) # 0 <= r <2**k而 r >= n:r = getrandbits(k)返回对于 xrange(len(x) - 1, 0, -1) 中的 i:# 在 x[:i+1] 中选择一个元素来交换 x[i]j = _randbelow(i+1)x[i], x[j] = x[j], x[i]

它在 2.7 上为您提供与 3.5 相同的输出:

<预><代码>>>>随机种子(42)>>>打印(随机.随机())0.639426798458>>>l = 列表(范围(20))>>>新闻洗牌(l)>>>打印(升)[3, 5, 2, 15, 9, 12, 16, 19, 6, 13, 18, 14, 10, 1, 11, 4, 17, 7, 8, 0]

I am writing a program compatible with both Python 2.7 and 3.5. Some parts of it rely on stochastic process. My unit tests use an arbitrary seed, which leads to the same results across executions and languages... except for the code using random.shuffle.

Example in Python 2.7:

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]

Same input in Python 3.5:

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]

Note that the pseudo-random number is the same, but the shuffled lists are different. As expected, reexecuting the cells does not change their respective output.

How could I write the same test code for the two versions of Python?

解决方案

In Python 3.2 the random module was refactored a little to make the output uniform across architectures (given the same seed), see issue #7889. The shuffle() method was switched to using Random._randbelow().

However, the _randbelow() method was also adjusted, so simply copying the 3.5 version of shuffle() is not enough to fix this.

That said, if you pass in your own random() function, the implementation in Python 3.5 is unchanged from the 2.7 version, and thus lets you bypass this limitation:

random.shuffle(l, random.random)

Note however, than now you are subject to the old 32-bit vs 64-bit architecture differences that #7889 tried to solve.

Ignoring several optimisations and special cases, if you include _randbelow() the 3.5 version can be backported as:

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]

which gives you the same output on 2.7 as 3.5:

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

这篇关于Python 2 和 3 之间的差异,用于使用给定种子进行洗牌的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆