Python 与其他算法相比,Heapsort算法的性能较差
我有两种Python 与其他算法相比,Heapsort算法的性能较差,python,algorithm,sorting,heapsort,Python,Algorithm,Sorting,Heapsort,我有两种heapsort算法。第一本是我写的,第二本是从某个网站上取的。根据我的说法,两者的逻辑是一样的,但第二种逻辑比第一种逻辑表现得更好。为什么会这样?我能看到的唯一区别是,我的使用递归,而另一个使用迭代。仅仅这一点就可以成为区别因素吗 我的代码: def heapify(arr,i,n): pivot = arr[i] #the value of the root node left,right = (i<<1)+1,(i<<1)+2 #ind
heapsort
算法。第一本是我写的,第二本是从某个网站上取的。根据我的说法,两者的逻辑是一样的,但第二种逻辑比第一种逻辑表现得更好。为什么会这样?我能看到的唯一区别是,我的使用递归,而另一个使用迭代。仅仅这一点就可以成为区别因素吗
我的代码:
def heapify(arr,i,n):
pivot = arr[i] #the value of the root node
left,right = (i<<1)+1,(i<<1)+2 #indices of the left and right subtree root nodes
if right <= n-1: #if right is within the array, so is left
if arr[left] <= pivot and arr[right] <= pivot:
return #if both are less than the root node, it's already heapified
maximum = left if arr[left] >= arr[right] else right #else find which child has a higher value
arr[maximum],arr[i] = arr[i],arr[maximum] #swap the root node with that child
return heapify(arr,maximum,n) #make the changed child the new root and recurse
else:
if left <= n-1: #right is outside the array, so check for left only
if arr[left] <= pivot:
return
arr[i],arr[left] = arr[left], arr[i] #same logic as above
return heapify(arr,left,n)
else:
return
def heapit(array,n):
for i in range((len(array)-1)/2,-1,-1): #all elements after (len(array)-1)/2 are the leaf nodes, so we have to heapify earlier nodes
heapify(array,i,n)
def heapsort(array):
n = len(array)
for i in range(n,0,-1):
heapit(array,i) #make the array a heap
array[0],array[i-1] = array[i-1],array[0] #swap the root node with the last element
这提供了相当的性能,但仍然较慢。对于一个100万美元的数组,结果几乎是20秒:4秒。还可以做些什么?编辑:我下面的评论可能会解释一个主要的减速,但最重要的是,您的算法不是heapsort 在函数
heapsort
中,对范围(n,0,-1)中的i执行循环。这就是n
迭代,其中n
是输入的大小。在该循环中,您调用heapit
,它为范围((len(array)-1)/2,-1,-1)中的i循环;这大概是n//2
迭代
n*(n//2)
=Θ(n
²)。换句话说,您有一个至少需要二次时间的算法,而第二个算法实现了真正的heapsort,它在O(n
lgn
)时间内运行
/EDIT
很可能是递归破坏了性能,再加上在模块级定义的函数调用。Python(至少是CPython)不是为递归程序而优化的,而是为迭代程序而优化的。对于heapify
中的每个递归调用,CPython必须执行以下七字节代码指令:
9 158 LOAD_GLOBAL 0 (heapify)
161 LOAD_FAST 0 (arr)
164 LOAD_FAST 6 (maximum)
167 LOAD_FAST 2 (n)
170 CALL_FUNCTION 3
173 RETURN_VALUE
>> 174 POP_TOP
(使用确定)。最后两条指令在递归调用完成后执行,因为
虽然这看起来并不昂贵,但是必须执行加载\u GLOBAL
来查找heapify
,并且heapify
、arr
、最大值
和i
的参考计数必须增加。当递归调用完成时,引用计数必须再次递减。函数调用在Python中非常昂贵
正如import this
所说,“扁平比嵌套好”:尽可能选择迭代而不是递归。什么尺寸?你有没有从统计数据上证实情况确实如此?如果是-通常的怀疑是缓存效率。对于相同的100000,实际时间的性能差异是什么?这两种算法遵循相似的逻辑,因此我猜缓存效率低下将适用于它们。关于大小,我只测试了10000个元素的列表。我的程序用了将近40秒,而另一个程序用了不到一秒。对于100000个程序,我的程序在将近15分钟内都没有返回提示,所以我就杀了它!另一个花了大约10秒。你的函数名很混乱。你的“heapify”算法应该是heapify的“涓流”步骤吗?很有趣,也是一个很好的答案。有那么重要吗?(从40秒到15分钟以上的时间差?)或者根据您个人在python中的经验,您认为这只是问题的一部分。同样的问题,有那么重要吗?@amit:这可能只是问题的一部分;当我反汇编第二个程序时,循环中没有LOAD\u GLOBAL
指令,这也可能是性能杀手。请注意,40秒与15分钟(假设程序即将完成)只是一个数量级的差异(22.5倍),因此,一些优化技巧应该足以比一个幼稚的程序获得如此大的加速。@Cupidvogel:我刚刚发现了另一件事。您使用的是Python2还是Python3?Python2.7。这可能是一个因素吗?
def heapsort(array):
n = len(array)
heapit(array,n)
array[n-1],array[0] = array[0],array[n-1]
for i in range(n-1):
heapify(array,0,n-1-i)
array[n-i-2],array[0] = array[0],array[n-i-2]
9 158 LOAD_GLOBAL 0 (heapify)
161 LOAD_FAST 0 (arr)
164 LOAD_FAST 6 (maximum)
167 LOAD_FAST 2 (n)
170 CALL_FUNCTION 3
173 RETURN_VALUE
>> 174 POP_TOP