Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/338.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 与其他算法相比,Heapsort算法的性能较差_Python_Algorithm_Sorting_Heapsort - Fatal编程技术网

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
lg
n
)时间内运行

/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