• 递归与分治


    递归

    递归的定义

    直接递归调用:

    间接递归调用:

      编写递归函数时,必须告诉它何时停止递归。正因为如此,每个递归函数都有两部分:基线条件(base case)和递归条件(recursive case)。递归条件指的是函数调用自己,而基线条件则指的是函数不再调用自己,从而避免形成无限循环。
      我们来给函数countdown添加基线条件。

    def countdown(i): 
        print i 
        if i <= 0:#基线条件
            return 
        else:#递归条件
            countdown(i-1) 
    

     

     

     

     非递归实现阶乘函数:

    递归程序:

    def f(n):
        if n<=1:
            return n
        return f(n-1)+f(n-2)
    

     非递归程序(两种方式):

    def Fibonacci(self, n):
            a = [0,1]
            if n<2:
                return a[n]
            for i in range(2,n+1):
                a.append(a[i-1]+a[i-2])
            return a[n]
    
    def f(n):
        if n<=1:
            return n
        else:
            pre,now = 0,1
            for i in range(2,n+1):
                next = pre + now
                pre = now
                now = next
            return next
    

    #递归
    def isHuiWen(str):
        if len(str)<=1:
            return True
        if str[0] !=str[-1]:
            return False
        return isHuiWen(str[1:-1])
    print(isHuiWen('qwswq'))
    

     

    #非递归
    def isHuiWen2(str):
        if len(str)<=1:
            return True
        for i in range(len(str)):
            if str[i] != str[len(str)-1-i]:
                return False
        return True
    

     注:调用栈不仅对编程来说很重要,使用递归时也必须理解这个概念

    分治

      上部分深入介绍了递归,以下使用学到的新技能来解决问题。我们将探索分而治(divide and conquer,D&C)——一种著名的递归式问题解决方法。
      快速排序。快速排序是一种排序算法,属于D&C算法。

    分而治之

      D&C并不那么容易掌握,首先,介绍一个直观的示例;然后,介绍一个代码示例,它不那么好看,但可能更容易理解
      假设你是农场主,有一小块土地。

      你要将这块地均匀地分成方块,且分出的方块要尽可能大。显然,下面的分法都不符合要求。

      如何将一块地均匀地分成方块,并确保分出的方块是最大的呢?使用D&C策略!D&C算法是递归的。使用D&C解决问题的过程包括两个步骤。
        (1) 找出基线条件,这种条件必须尽可能简单。
        (2) 不断将问题分解(或者说缩小规模),直到符合基线条件。
      下面就来使用D&C找出前述问题的解决方案。可你能使用的最大方块有多大呢?
      首先,找出基线条件。最容易处理的情况是,一条边的长度是另一条边的整数倍。
      如果一边长25 m,另一边长50 m,那么可使用的最大方块为 25 m×25 m。换言之,可以将这块地分成两个这样的方块。
      现在需要找出递归条件,这正是D&C的用武之地。根据D&C的定义,每次递归调用都必须缩小问题的规模。如何缩小前述问题的规模呢?我们首先找出这块地可容纳的最大方块。

      你可以从这块地中划出两个640 m×640 m的方块,同时余下一小块地。现在是顿悟时刻:何不对余下的那一小块地使用相同的算法呢?
      最初要划分的土地尺寸为1680 m×640 m,而现在要划分的土地更小,为640 m×400 m。适用于这小块地的最大方块,也是适用于整块地的最大方块。换言之,你将均匀划分1680 m×640 m土地的问题,简化成了均匀划分640 m×400 m土地的问题!

      下面再次使用同样的算法。对于640 m × 400 m的土地,可从中划出的最大方块为400 m × 400 m。这将余下一块更小的土地,其尺寸为400 m × 240 m。

      你可从这块土地中划出最大的方块,余下一块更小的土地,其尺寸为240 m × 160 m。

      接下来,从这块土地中划出最大的方块,余下一块更小的土地。
      余下的这块土地满足基线条件,因为160是80的整数倍。将这块土地分成两个方块后,将不会余下任何土地!

      因此,对于最初的那片土地,适用的最大方块为80 m× 80 m。

      这里重申一下D&C的工作原理:
        (1) 找出简单的基线条件;
        (2) 确定如何缩小问题的规模,使其符合基线条件。
      D&C并非可用于解决问题的算法,而是一种解决问题的思路。我们再来看一个例子。

      给定一个数字数组。你需要将这些数字相加,并返回结果。使用循环很容易完成这种任务。

    def sum(arr): 
        total = 0 
        for x in arr: 
            total += x 
            return total 
    print sum([1, 2, 3, 4]) 
    

       但如何使用递归函数来完成这种任务呢?
      第一步:找出基线条件。最简单的数组什么样呢?请想想这个问题,再接着往下读。如果数组不包含任何元素或只包含一个元素,计算总和将非常容易。
      因此这就是基线条件。
      第二步:每次递归调用都必须离空数组更近一步。如何缩小问题的规模呢?下面是一种办法。
      这与下面的版本等效。
      这两个版本的结果都为12,但在第二个版本中,给函数sum传递的数组更短。换言之,这缩小了问题的规模!

      函数sum的工作原理类似于下面这样。

      这个函数的运行过程如下。

      别忘了,递归记录了状态。

    提 示:
      编写涉及数组的递归函数时,基线条件通常是数组为空或只包含一个元素。陷入困境时,请检查基线条件是不是这样的。

    注意:

    • 分治法设计算法时最好使子问题的大小规模相同,即平衡子问题的思想。
    • D&C将问题逐步分解。使用D&C处理列表时,基线条件很可能是空数组或只包含一个元素的数组。 

     

  • 相关阅读:
    软件工程14—第09组 Beta冲刺(2/4)
    软件工程13—第09组 Beta冲刺(1/4)
    软件工程12—第09组 Alpha事后诸葛
    软件工程11—第09组 Alpha冲刺(4/4)
    软件工程10—第09组 Alpha冲刺(3/4)
    软件工程09—第09组 Alpha冲刺(2/4)
    软件工程08—第09组 Alpha冲刺(1/4)
    软件工程07—第09组 团队Git现场编程实战
    软件工程06—亚瑟王の十三水2.0
    第06组 Alpha冲刺(4/6)
  • 原文地址:https://www.cnblogs.com/tianqizhi/p/9571958.html
Copyright © 2020-2023  润新知