【问题标题】:Converting a recursion to iteration in python在python中将递归转换为迭代
【发布时间】:2015-10-25 06:22:58
【问题描述】:

我编写了以下 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])

【问题讨论】:

  • 你确定这段代码能正常工作吗?您的方法结束时有两个调用 - 只有一个实际正在执行。
  • 他们都被调用了。一个用参数 A,first,j 调用,第二个用参数 A,i,last 调用
  • 一旦第一个被调用,第二行永远不会执行。就像有两个 return 语句。
  • @Burhan。你是不正确的。第一个完成后,它返回到代码中的那个位置。然后执行第二个调用。 return 语句是相关的,但功能不同。

标签: python recursion iteration quicksort


【解决方案1】:

您始终可以将 尾递归 算法(即递归步骤是函数中的最后一条语句 的算法)更改为迭代算法。在 Python 中,迭代几乎总是比等效的尾递归快,因为 Python(故意)缺少称为尾调用优化的功能,因为 Guido van Rossum 认为在优化中丢失的调试信息比获得的速度更重要。其他语言做出了相反的权衡,因此在其中一些语言中,递归版本可能是首选。

然而,快速排序不是(仅)尾递归:它确实作为它所做的最后一件事进行递归,但它作为它所做的倒数第二件事进行递归.将这种算法转换为迭代算法的唯一通用方法是将大量状态存储在堆栈上 - 本质上是重新实现函数调用的工作方式。这会通过通常在幕后完成的“内务处理”污染您的代码,并且通常会使事情变得相当慢(因为堆栈管理是通过函数调用完成的,所以必须完成幕后工作无论如何,你正在复制它)。

对于某些特定算法,可能有一种方法可以将非尾递归干净地转换为迭代,但通常您最终会得到具有不同性能特征的不同算法,因此它最终不会成为迭代和递归性能之间的比较。

特别是对于快速排序,递归版本更可取,您几乎不会“在野外”看到它的迭代版本,除非演示如何使用堆栈。例如,请参阅this blog post about recursive and iterative quicksort - 迭代版本使用堆栈,并给出以下结果摘要:

您可以看到,该分析声称迭代版本对于每个元素计数都较慢,尽管随着列表变大,相对而言差异似乎越来越小。另请注意,(高度优化的)Python 内置函数list.sort 的性能优于两种快速排序实现 - 如果您特别关心速度(而不是编写自己的快速排序的学习经验),请每次都使用内置函数。

【讨论】:

  • 感谢您的链接。我想要快速排序的学习经验。 :) 内置方法使用合并排序和“疾驰排序”的组合,并且非常快。我参考了内置排序的 C 代码。
【解决方案2】:

是的,有一种方法可以转换它。是的,迭代几乎总是在执行时间上胜过递归。但是,递归通常更易于阅读和维护,从而节省了程序员的时间并减少了错误的发生率。

递归通常较慢,因为与维护计数器和一些状态变量相比,函数调用相对昂贵。当有很多元素时,这种差异实际上会变得更大。

您可能将此问题(迭代与递归)与计算复杂性问题混淆了。许多分而治之的算法将一个操作从 O(n) 减少到 O(log n),其中许多算法便于递归编写。例如,快速排序是 O(n log n),但更简单的冒泡排序是 O(n^2)。快速排序很容易递归编写;冒泡排序更容易通过迭代编写。但是,它们仍然不是等效算法。

【讨论】:

    猜你喜欢
    • 2019-10-10
    • 2016-01-26
    • 2017-03-05
    • 2015-08-22
    • 2021-03-11
    • 1970-01-01
    相关资源
    最近更新 更多