Python 为什么max比sort慢?
我发现Python 为什么max比sort慢?,python,sorting,max,python-internals,Python,Sorting,Max,Python Internals,我发现max比Python2和3中的sort函数慢 Python 2 $ python -m timeit -s 'import random;a=range(10000);random.shuffle(a)' 'a.sort();a[-1]' 1000 loops, best of 3: 239 usec per loop $ python -m timeit -s 'import random;a=range(10000);random.shuffle(a)' 'max(a)'
max
比Python2和3中的sort
函数慢
Python 2
$ python -m timeit -s 'import random;a=range(10000);random.shuffle(a)' 'a.sort();a[-1]'
1000 loops, best of 3: 239 usec per loop
$ python -m timeit -s 'import random;a=range(10000);random.shuffle(a)' 'max(a)'
1000 loops, best of 3: 342 usec per loop
Python 3
$ python3 -m timeit -s 'import random;a=list(range(10000));random.shuffle(a)' 'a.sort();a[-1]'
1000 loops, best of 3: 252 usec per loop
$ python3 -m timeit -s 'import random;a=list(range(10000));random.shuffle(a)' 'max(a)'
1000 loops, best of 3: 371 usec per loop
为什么
max
(O(n)
)比sort
函数(O(nlogn)
)慢?在Python中使用timeit
模块时必须非常小心
python -m timeit -s 'import random;a=range(10000);random.shuffle(a)' 'a.sort();a[-1]'
在这里,初始化代码运行一次以生成随机数组a
。然后,代码的其余部分将运行几次。第一次对数组进行排序时,但每隔一次对已排序的数组调用sort方法时。只返回最快的时间,因此实际上您正在计时Python对已排序的数组进行排序所需的时间
Python排序算法的一部分是检测数组何时已经部分或完全排序。当完全排序后,它只需在数组中扫描一次即可检测到这一点,然后停止
如果您尝试过:
python -m timeit -s 'import random;a=range(100000);random.shuffle(a)' 'sorted(a)[-1]'
然后在每个定时循环上进行排序,您可以看到对数组进行排序的时间确实比只找到最大值要长得多
编辑:@skyking's解释了我未解释的部分:
a.sort()
知道它正在处理列表,因此可以直接访问元素max(a)
适用于任何任意iterable,因此必须使用泛型迭代。这可能是因为l.sort
是列表的成员,而max
是泛型函数。这意味着l.sort
可以依赖list
的内部表示,而max
必须通过通用迭代器协议
这使得l.sort
的每个元素提取都比max
的每个元素提取快
我假设如果您改为使用排序(a)
,您将得到比max(a)
慢的结果,请注意,虽然。显然,使用迭代器是一项重要的开销,这就是为什么您会观察到计时上的差异
然而,除此之外,你的测试是不公平的。您在同一列表上多次运行a.sort()
。专门设计用于快速处理已(部分)排序的数据。您的测试表明该算法运行良好
这些都是公平的测试:
$ python3 -m timeit -s 'import random;a=list(range(10000));random.shuffle(a)' 'max(a[:])'
1000 loops, best of 3: 227 usec per loop
$ python3 -m timeit -s 'import random;a=list(range(10000));random.shuffle(a)' 'a[:].sort()'
100 loops, best of 3: 2.28 msec per loop
在这里,我每次都要创建一份列表。正如您所看到的,结果的数量级是不同的:正如我们所期望的,微与毫秒
记住:大Oh指定一个上限!Python排序算法的下限是Ω(n)。O(n logn)并不自动意味着每次运行都需要与n logn成比例的时间。它甚至不意味着它需要比O(n)算法慢,但这是另一回事。需要了解的是,在某些有利的情况下,O(n logn)算法可能会在O(n)时间或更短的时间内运行。
a.sort()
在适当的位置工作。尝试sorted(a)
@AndreaCorbellini,但sorted(a)需要O(n)
内存,max(a)只需要one@WeizhongTu但是,sort
sort,然后,a
进行排序,这也是值得注意的:python使用了Timsort。此算法对已排序的列表执行n-1
比较,这与max
必须执行的数字相同。事实上,即使输入是“部分排序”的,Timsort也会进行O(n)比较。其他算法可能需要O(nlogn)时间,即使在已排序的情况下也是如此。我从来没有意识到解释器状态在代码运行期间是保留的。现在我想知道我在过去制作了多少错误的基准测试。:-}这对我来说是显而易见的。但请注意,即使对已排序的数组进行排序,也必须检查所有元素。这和获得最大值的工作量一样多。。。。在我看来,这似乎是半个答案。@KarolyHorvath,你是对的。我认为@skyking得到了另一半答案:a.sort()
知道它正在处理列表,因此可以直接访问元素max(a)
在任意序列上工作,不需要使用泛型迭代。@KarolyHorvath也许分支预测可以解释为什么重复排序排序的数组更快:@JUnitorCompressor解释“它在许多类型的偏序数组上具有超自然的性能(比需要的lg(N!)比较少,并且只有N-1次)”然后继续解释各种血腥的优化。我认为它可以做出许多max
无法做到的假设,即排序不是渐进地快。这个假设离变得更具体只有一行时间了。不要质疑你的知识,只是这样一个添加对于那些不知道它的人来说是微不足道的。你是正确的,sorted(a)
比max(a)
慢。毫不奇怪,它的速度与a.sort()
的速度差不多,但您对其原因的猜测——这是因为OP在测试中犯了一个错误,正如公认的答案所指出的。问题是,通用迭代器协议有可能有足够的开销来抵消日志(n)
将复杂性考虑在内。这是一个O(n)
算法,对于足够大的n
(例如,因为每个操作的时间在算法之间可能不同-nlogn
快速步骤可能比n
慢速步骤快)。在这种情况下,没有考虑盈亏平衡的确切位置(但应该注意,logn
因子对于smallishn
来说不是一个很大的因子)。