8

我刚刚观察到,当使用 Python3 显式提交关键字参数的函数时,列表的改组需要大约random.shuffle一半的运行时间。我检查了Python2是否有同样的问题,但发现它只发生在Python3上。random.randomrandom

我使用以下代码来测量两个版本的运行时间:

from timeit import Timer
t1 = Timer("random.shuffle(l)", "import random; l = list(range(100000))")
t2 = Timer("random.shuffle(l, random = random.random)", "import random; l = list(range(100000))")
print("With default rand: %s" % t1.repeat(10,1))
print("With custom rand: %s" % t2.repeat(10,1))

在 ideone制作了一个测试用例,供您使用 Python3 和使用 Python2 的相同代码查看

根据shuffle 的文档,random.random当我省略可选关键字参数时,默认情况下使用相同的函数random,因此当我给它提供与默认情况下相同的生成随机数的函数时应该没有区别。

shuffle我检查了文件夹中函数的相应来源(Python2 与 Python3),Lib/random.py发现如果我使用关键字函数显式调用 Python3 版本,它们的行为方式相同random。如果我省略这个参数,Python3 使用辅助函数_randbelow,所以应该是我的问题的根源。我不明白为什么 Python3 使用它,_randbelow因为它变慢shuffle了。据我了解,它的好处在于生成任意大的随机数,但它不应该减慢我对少于 2^32 个元素(在我的情况下为 100000)的列表的改组。

任何人都可以向我解释为什么我在运行时看到如此不同,尽管当我使用 Python3 时它们应该更接近?

PS:请注意,我不感兴趣为什么使用 Python2 的运行时比使用 Python3 更好,但是在 Python3 中使用参数rand=rand.rand参数与仅在 Python3 中不使用它时运行时的差异。

4

1 回答 1

4

函数中的文档字符串random.shuffle与代码相矛盾。在 python 2.7.2+ 中,文档字符串是正确的:

    def shuffle(self, x, random=None, int=int):
    """x, random=random.random -> shuffle list x in place; return None.

    Optional arg random is a 0-argument function returning a random
    float in [0.0, 1.0); by default, the standard random.random.
    """

    if random is None:
        random = self.random
    for i in reversed(xrange(1, len(x))):
        # pick an element in x[:i+1] with which to exchange x[i]
        j = int(random() * (i+1))
        x[i], x[j] = x[j], x[i]

但在 Python 3.2 中,我们发现:

def shuffle(self, x, random=None, int=int):
    """x, random=random.random -> shuffle list x in place; return None.

    Optional arg random is a 0-argument function returning a random
    float in [0.0, 1.0); by default, the standard random.random.
    """

    randbelow = self._randbelow
    for i in reversed(range(1, len(x))):
        # pick an element in x[:i+1] with which to exchange x[i]
        j = randbelow(i+1) if random is None else int(random() * (i+1))
        x[i], x[j] = x[j], x[i]

所以文档字符串仍然讲述了旧故事,但现在使用的默认函数是 random.randbelow

于 2013-02-27T19:13:56.133 回答