【发布时间】:2020-06-26 21:38:36
【问题描述】:
使用random.shuffle,我注意到改组list(range(n)) 比改组[0] * n 多花费25% 的时间。以下是大小 n 从 100 万到 200 万的时间:
为什么洗牌list(range(n)) 变慢了?与排序列表(需要查看对象)或复制列表(增加对象内部的引用计数器)不同,对象在这里应该无关紧要。这应该只是重新排列列表内的指针。
我还尝试了numpy.random.shuffle,其中改组list(range(n)) 比改组[0] * n 慢三倍(!):
我还尝试了第三种方法来重新排列列表中的元素,即list.reverse。正如预期的那样,这两个列表花费了同样长的时间:
为了以防洗牌顺序很重要,我在洗牌后也尝试了list.reverse。同样,正如预期的那样,这两个列表花费了同样长的时间,并且在没有事先改组的情况下也同样长:
那么有什么区别呢? shuffle 和 reversing 都只需要重新排列列表内的指针,为什么对象对 shuffle 很重要,而对 reversing 不重要?
我的基准代码生成时间:
import random
import numpy
from timeit import repeat, timeit
from collections import defaultdict
shufflers = {
'random.shuffle(mylist)': random.shuffle,
'numpy.random.shuffle(mylist)': numpy.random.shuffle,
'list.reverse(mylist)': list.reverse,
}
creators = {
'list(range(n))': lambda n: list(range(n)),
'[0] * n': lambda n: [0] * n,
}
for shuffler in shufflers:
print(shuffler)
for creator in creators:
print(creator)
times = defaultdict(list)
for _ in range(10):
for i in range(10, 21):
n = i * 100_000
mylist = creators[creator](n)
# Uncomment next line for pre-shuffling
# numpy.random.shuffle(mylist)
time = timeit(lambda: shufflers[shuffler](mylist), number=1)
times[n].append(time)
s = '%.6f ' * len(times[n])
# Indent next line further to see intermediate results
print([round(min(times[n]), 9) for n in sorted(times)])
【问题讨论】:
-
也许改组通过交换条目来工作,代码检查值是否不同,如果它们相同则不交换?你试过看
random.shuffle的源码吗? -
@barny 它不会那样做。但你确实可以在那里找到大约 40% 的解释。
-
好奇 PyPy3 上的图是什么样子的...
标签: python performance shuffle