Python中最快的排序方式
Posted
技术标签:
【中文标题】Python中最快的排序方式【英文标题】:Fastest way to sort in Python 【发布时间】:2011-04-20 19:27:27 【问题描述】:在 Python 中对大于 0 且小于 100000 的整数数组进行排序的最快方法是什么?但不使用 sort 等内置函数。
我正在研究根据输入大小组合 2 个运动功能的可能性。
【问题讨论】:
为什么不使用内置函数? 什么是最快的到达道路但不驾驶保时捷的方法? @Anders:不要重新发明***。内置的 sort() 应该足以满足您的情况。 我很好奇:为什么需要实现自己的排序例程?这对我来说就像是家庭作业:-) @Aaron - 或者更重要的是 - 在不使用任何类型的预制车辆的情况下,最快的方式是什么;) 【参考方案1】:桶大小 = 1 的桶排序。内存为 O(m),其中 m = 被排序的值的范围。运行时间为 O(n),其中 n = 被排序的项目数。当用于记录计数的整数类型有界时,如果任何值出现超过 MAXINT 次,此方法将失败。
def sort(items):
seen = [0] * 100000
for item in items:
seen[item] += 1
index = 0
for value, count in enumerate(seen):
for _ in range(count):
items[index] = value
index += 1
【讨论】:
【参考方案2】:我可能有点迟到了,但是https://www.linkedin.com/pulse/sorting-efficiently-python-lakshmi-prakash有一篇有趣的文章比较了不同的种类
其中一个主要收获是,虽然默认排序效果很好,但我们可以使用快速排序的编译版本做得更好。这需要 Numba 包。
这里是 Github 存储库的链接: https://github.com/lprakash/Sorting-Algorithms/blob/master/sorts.ipynb
【讨论】:
【参考方案3】:@fmark 我针对来自http://rosettacode.org/wiki/Sorting_algorithms/Quicksort#Python 的python 快速排序编写的python 合并排序实现的一些基准测试 并从最佳答案。
-
列表的大小与列表中数字的大小无关
合并排序获胜,但是它使用内置的 int() 来地板
import numpy as np
x = list(np.random.rand(100))
# TEST 1, merge_sort
def merge(l, p, q, r):
n1 = q - p + 1
n2 = r - q
left = l[p : p + n1]
right = l[q + 1 : q + 1 + n2]
i = 0
j = 0
k = p
while k < r + 1:
if i == n1:
l[k] = right[j]
j += 1
elif j == n2:
l[k] = left[i]
i += 1
elif left[i] <= right[j]:
l[k] = left[i]
i += 1
else:
l[k] = right[j]
j += 1
k += 1
def _merge_sort(l, p, r):
if p < r:
q = int((p + r)/2)
_merge_sort(l, p, q)
_merge_sort(l, q+1, r)
merge(l, p, q, r)
def merge_sort(l):
_merge_sort(l, 0, len(l)-1)
# TEST 2
def quicksort(array):
_quicksort(array, 0, len(array) - 1)
def _quicksort(array, start, stop):
if stop - start > 0:
pivot, left, right = array[start], start, stop
while left <= right:
while array[left] < pivot:
left += 1
while array[right] > pivot:
right -= 1
if left <= right:
array[left], array[right] = array[right], array[left]
left += 1
right -= 1
_quicksort(array, start, right)
_quicksort(array, left, stop)
# TEST 3
def qsort(inlist):
if inlist == []:
return []
else:
pivot = inlist[0]
lesser = qsort([x for x in inlist[1:] if x < pivot])
greater = qsort([x for x in inlist[1:] if x >= pivot])
return lesser + [pivot] + greater
def test1():
merge_sort(x)
def test2():
quicksort(x)
def test3():
qsort(x)
if __name__ == '__main__':
import timeit
print('merge_sort:', timeit.timeit("test1()", setup="from __main__ import test1, x;", number=10000))
print('quicksort:', timeit.timeit("test2()", setup="from __main__ import test2, x;", number=10000))
print('qsort:', timeit.timeit("test3()", setup="from __main__ import test3, x;", number=10000))
【讨论】:
【参考方案4】:如果您对渐近时间感兴趣,那么计数排序或基数排序可以提供良好的性能。
但是,如果您对挂钟时间感兴趣,您将需要使用您的特定数据集比较不同算法之间的性能,因为不同的算法在不同的数据集上表现不同.在这种情况下,它总是值得尝试快速排序:
def qsort(inlist):
if inlist == []:
return []
else:
pivot = inlist[0]
lesser = qsort([x for x in inlist[1:] if x < pivot])
greater = qsort([x for x in inlist[1:] if x >= pivot])
return lesser + [pivot] + greater
来源:http://rosettacode.org/wiki/Sorting_algorithms/Quicksort#Python
【讨论】:
很好的建议,除了变量列表的选择,这可能会导致很好的错误。我发布了其他更快的版本。 对同一组变量运行两次列表推导可能也不是最佳的。 @Tony Veijalainen “计算机科学中只有两件难事:缓存失效和命名”——我已经更改了变量名【参考方案5】:def sort(l):
p = 0
while(p<len(l)-1):
if(l[p]>l[p+1]):
l[p],l[p+1] = l[p+1],l[p]
if(not(p==0)):
p = p-1
else:
p += 1
return l
这是我创建的算法,但速度非常快。只是做排序(l) l 是您要排序的列表。
【讨论】:
冒泡排序【参考方案6】:从理论上讲,基数排序以线性时间运行(排序时间大致与数组大小成正比增长),但实际上快速排序可能更适合,除非您要对绝对庞大的数组进行排序。
如果想让快速排序快一点,可以在数组变小时使用插入排序]。
理解算法复杂性和 Big-O 表示法的概念可能也会有所帮助。
【讨论】:
当你说数组变小是指小于64? 我会说少于 10 个,但没有正确答案;最好的办法是尝试不同的值,看看哪个更快。【参考方案7】:我们可以使用字典的计数排序来最小化额外的空间使用,并保持运行时间低。由于 python 与 C 的实现开销,对于小尺寸的输入数组,计数排序要慢得多。当数组(COUNT)的大小约为 100 万时,计数排序开始超过常规排序。
如果您真的想为较小尺寸的输入带来巨大的加速,请在 C 中实现计数排序并从 Python 中调用它。
(修复了 Aaron (+1) 帮助捕获的错误...) 下面的仅 python 实现比较了这两种方法...
import random
import time
COUNT = 3000000
array = [random.randint(1,100000) for i in range(COUNT)]
random.shuffle(array)
array1 = array[:]
start = time.time()
array1.sort()
end = time.time()
time1 = (end-start)
print 'Time to sort = ', time1*1000, 'ms'
array2 = array[:]
start = time.time()
ardict =
for a in array2:
try:
ardict[a] += 1
except:
ardict[a] = 1
indx = 0
for a in sorted(ardict.keys()):
b = ardict[a]
array2[indx:indx+b] = [a for i in xrange(b)]
indx += b
end = time.time()
time2 = (end-start)
print 'Time to count sort = ', time2*1000, 'ms'
print 'Ratio =', time2/time1
【讨论】:
+1Ratio = 1.16710428623
在我的机器上。巧妙地使用字典。值得注意的是,将 dict 构造阶段从 try: ardict[a] += 1; except: ardict[a] = 1
更改为 if a in ardict: ardict[a] += 1; else: ardict[a] = 1
会将比率降低到 Ratio = 0.696179723863
有时(通常)最好在跳跃之前先查看。我知道这样做是因为try
仅比if
便宜,如果很少发生异常。一个实际的例外仍然非常昂贵。
很遗憾这个算法是错误的。试试array = [1,10, 100, 1000, 10000, 100000, 1000000]
。在未记录的实施细节上滑冰的危险再次发生。
感谢 aaron - 修复了不排序 dict 键的错误。那应该放慢一点。但是,如果与数组大小相比,不同元素的数量较低,它将保持其几乎 O(n) 的性质。我希望看到不同元素的 3D 图,数组长度为 x 和 y 维度以及运行时间与第 3 维度的比率。也许我会在一两天内完成。
@Aaron:在我的比赛中,(尝试,除外)效果更好。我先用(if then else)方式对其进行编码,然后切换到(try, except)以加快速度。此外,删除错误的版本比前一个版本运行得更快 - 因为在排序键中使用了底层 C 排序。那是免费的啤酒。 :-)
对我来说,例外解决方案也更快,如果不需要生成列表(因为它是可变的),则使用带有生成器的元组生成更快:array2=tuple(a for a in sorted(ardict) for i in xrange(ardict[a]))
【参考方案8】:
Python 的早期版本混合使用了samplesort
(一种具有大样本量的快速排序变体)和二进制插入排序作为内置排序算法。这被证明有些不稳定。 S0,从 python 2.3 开始使用adaptive mergesort
算法。
归并排序的顺序(平均)=O(nlogn)
。
合并排序顺序(最差)=O(nlogn)
。
但是快速排序的顺序(最差)= n*2
如果你使用list=[ .............. ]
list.sort()
使用mergesort algorithm.
对于排序算法之间的比较,您可以阅读wiki
详细对比comp
【讨论】:
是timsort,比mergesort更具适应性 Timsort 是一种自适应、稳定、自然的归并排序。【参考方案9】:内置函数是最好的,但是因为你不能使用它们,所以看看这个:
http://en.wikipedia.org/wiki/Quicksort
【讨论】:
【参考方案10】:既然你知道数字的范围,你可以使用Counting Sort,它在时间上是线性的。
【讨论】:
(我没有投反对票)。请注意,如果整数数组明显小于 100000,这不是一个好的算法,因为它会浪费内存(从而浪费时间)来构造 100000 个元素的列表。以上是关于Python中最快的排序方式的主要内容,如果未能解决你的问题,请参考以下文章