我编写了下面的python脚本,它使用分而治之(递归调用)对数组的元素进行排序。我的一位朋友建议递归比迭代慢。 有没有办法将以下程序转换为' for'循环并仍然利用分而治之的策略。即使列表包含很多元素,迭代也会击败递归吗?
### Using recursion
import random
from datetime import datetime
start = str(datetime.now().time()).split(':')
def quicksort(A,first,last):
print "calling parameters",A,first,last
if first >= last:
return
i , j = first, last
pivot = A[random.randint(first,last)]
#pivot = A[last]
while i <= j:
while A[i] < pivot:
i+=1
#print "i:",i
while A[j] > pivot:
j-=1
#print "i,j",i,j
if i <= j:
A[i],A[j] = A[j],A[i]
i,j = i+1, j-1
#print "intermediate",A
#print "loop i,j",i,j
# Using Recursion here
quicksort(A,first,j)
quicksort(A,i,last)
A = [2,8,7,1,3,5,6,4]
#A = [1,1,1,1,1,1,1,1]
quicksort(A,0,len(A)-1)
print A
stop = str(datetime.now().time()).split(':')
print "time taken",float(stop[2]) - float(start[2])
答案 0 :(得分:2)
您始终可以将尾递归算法(即递归步骤是函数中最后一个语句的算法)更改为迭代算法。在Python中,迭代几乎总是比等效的尾递归更快,因为Python(故意)缺少称为尾调用优化的功能,因为Guido van Rossum认为在优化中丢失的调试信息比获得的速度更重要。其他语言做出了相反的权衡,因此在某些语言中,递归版本可能是首选。
然而,快速排序不是(仅)尾递归:它确实作为最后一件事递归,但它也作为它做的第二件事递归。将这种算法转换为迭代算法的唯一通用方法是在堆栈上存储大量状态 - 实质上,重新实现函数调用的工作方式。这通常会在幕后进行的“内务管理”污染您的代码,并且通常会使事情变得相当慢(因为堆栈管理完成了,函数调用,所以必须完成幕后工作)无论如何,你正在重复它)。
对于某些特定的算法,可能有一种方法可以完全从非尾递归转换为迭代,但通常你最终得到的是具有不同性能特征的不同算法,所以它不会最终成为迭代和递归性能之间的比较。
特别是对于quicksort,递归版本更可取,除了演示如何使用堆栈之外,你很少会在“狂野”中看到它的迭代版本。例如,请参阅this blog post about recursive and iterative quicksort - 迭代版本使用堆栈,并提供结果摘要:
您可以看到此分析声称迭代版本对于每个元素计数都较慢,尽管随着列表变大,相对而言差异似乎变小。另请注意,(高度优化的)Python内置list.sort
优于快速排序实现一个数量级 - 如果您特别关注速度(而不是编写自己的快速排序的学习经验),请每次使用内置
答案 1 :(得分:0)
是的,有一种方法可以转换它。是的,迭代几乎总是胜过执行时间的递归。但是,递归通常更容易阅读和维护,从而节省了程序员的时间并减少了错误的发生率。
递归通常较慢,因为与维护计数器和一些状态变量相比,函数调用相对昂贵。当存在大量元素时,这种差异实际上会变大。
您可能会将此问题(迭代与递归)与计算复杂性问题混淆。许多分而治之的算法将一个操作从O(n)减少到O(log n),并且这些算法中的许多算法便于递归写入。例如,快速排序是O(n log n),但更简单的冒泡排序是O(n ^ 2)。快速排序很容易递归写入;使用迭代更容易编写冒泡排序。但是,它们仍然不是等效算法。