• Python基础——递归及其经典例题(阶乘、斐波那契数列、汉诺塔)



    什么是递归

    递归,从原理上讲,就是函数直接或间接地调用自身的算法。是重复调用函数自身实现循环,遇到满足终止条件的情况时逐层返回结束循环,是一种算法结构。

    递归在日常编程中有很多例子,例如谢尔宾斯基三角形:
    在这里插入图片描述



    阶乘

    正整数的阶乘是指从1乘以2乘以3乘以4一直盛到所要求的的数。例如,所要求的数是5的阶乘,则阶乘式是1×2×3×4×5,得到的积是120,所以120就是5的阶乘。

    非递归版本:

    # 计算阶乘(非递归)
    def recrusion(n):
        result = n
        for i in range(1, n):
            result *= i
        return result
    
    number = int(input('请输入一个整数:'))
    result = recrusion(number)
    print("%d 的阶乘是:%d" % (number, result))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    其执行结果如下:

    在这里插入图片描述


    递归版本:

    # 计算阶乘(递归)
    def recrusion(n):
        if n == 1:
            return 1
        else:
            return n*recrusion(n-1)
    
    number = int(input('请输入一个整数:'))
    result = recrusion(number)
    print("%d 的阶乘是:%d" % (number, result))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    其执行结果为:

    在这里插入图片描述


    递归函数的实现详细分析如下图所示:
    在这里插入图片描述


    这个例子满足了递归的两个条件
    (1)调用函数本身
    (2)设置了正确的返回条件



    斐波那契数列

    再介绍一个递归的经典例题——斐波那契数列。斐波那契数列的发明者,是意大利数学家列昂纳多·斐波那契(Leonardo Fibonacci)。其大致描述是这样的:
    假如有一对刚出生的小兔子,只需要一个月小兔子就能长成大兔子,从第三个月开始,这对大兔子每个月都会生下一对小兔子。新出生的小兔子又会花一个月长大,再花一个月开始生兔子…也就是说,兔子在出生两个月后,就有繁殖能力,在拥有繁殖能力后,这对兔子每个月能生出一对小兔子来, 如果每对兔子都经历这样的出生、成熟、生育的过程,并且永远不死,那么每个月兔子的总数是多少呢?

    在这里插入图片描述

    第一个月只有1对兔宝宝。

    第二个月只有1对大兔子。

    第三个月大兔子生了一对兔宝宝,一大一小2对兔子。

    第四个月大兔子继续生一对兔宝宝,小兔子变成大兔子。两大一小3对兔子。

    ….

    于是,我们可以得到每个月兔子的总数:

    所经过的月数123456789101112
    1123581321345589144

    可以用数学函数来定义:
    F ( n ) = { 1 , 当n=1时 1 , 当n=2时 F ( n − 1 ) + F ( n − 2 ) , 当n>2时 F(n)=

    {1当n=1时1当n=2时F(n1)+F(n2)当n>2时
    F(n)=11F(n1)+F(n2)n=1n=2n>2

    递归实现:

    # 实现斐波那契数列
    def fab(n):
        if n < 1:
            print('输入有误!')
            return -1
        if n == 1 or n == 2:
            return 1
        else:
            return fab(n-1) + fab(n-2)
    
    result = fab(12)
    if result != -1:
        print("第12个月共有%d对兔子" % result)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    其执行结果为144。

    递归的逻辑很简单,但是递归如果使用不当,效率会很低。因为递归的实现是函数自己调用自身,每次函数的调用都需要进行压栈、出栈、保存和恢复寄存器的操作,所以在这上边是非常消耗时间和空间的。



    汉诺塔

    法国数学家爱德华·卢卡斯曾编写过一个印度的古老传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔。不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金片:一次只移动一片,不管在哪根针上,小片必须在大片上面。僧侣们预言,当所有的金片都从梵天穿好的那根针上移到另外一根针上时,世界就将在一声霹雳中消灭,而梵塔、庙宇和众生也都将同归于尽。

    要解决这个问题,其思路如下:
    (1)将前63个盘子从A移动到B上,确保大盘在小盘底下。
    (2)将最底下的第64个盘子从A移动到C上。
    (3)将B上的63个盘子移动到C上。

    在这里插入图片描述


    解决问题的思路有了,关键在于步骤(1)和步骤(3)如何执行。由于每次只能移动一个圆盘,所以在移动过程中,显然要借助另外一根针才可以实施。也就是说,步骤(1)将1~63个盘子需要借助C移到B上步骤(3)将B上的63个盘子借助A移动到C上

    所以,把新的思路聚集为以下两个问题:
    问题一:将A上的63个盘子借助C移动到B上;
    问题二:将B上的63个盘子借助A移动到C上。

    而相应的问题一和问题二的解决方法跟刚才第一个问题的思路是一样的

    问题一(将A上的63个盘子借助C移动到B上)可拆解为:
    (1)将前62个盘子从A移动到C上,确保大盘在小盘下。
    (2)将最底下的第63个盘子移动到B上。
    (3)将C上的62个盘子移动到B上。
    问题二(将B上的63个盘子借助A移动到C上)可拆解为:
    (1)将前62个盘子从B移动到A上,确保大盘在小盘下。
    (2)将最底下的第63个盘子移动到C上。
    (3)将A上的62个盘子移动到C上。

    所以,这个难题可以用递归来解决:

    # 汉诺塔问题
    def hanoi(n, a, b, c):  # 将第n层从a借助b移动到c
        if n == 1:
            print(a, '-->', c) # 如果只有一层,直接从a移动到c
        else:
            hanoi(n-1, a, c, b) # 将前n-1个盘子从a移动到b上
            print(a, '-->', c) # 将最底下的第64个盘子从a移动到c上
            hanoi(n-1, b, a ,c) # 将b上的63个盘子移动到c上
    
    n = int(input('请输入汉诺塔的层数:'))
    hanoi(n, 'A', 'B', 'C')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • 相关阅读:
    ARM指令英文全称-速记ARM指令
    什么是 API ?
    如何做代码评审(code review)
    【虹科新品】 HK-MR430&330绝对式光纤编码器(上)
    嵌入式实时操作系统的设计与开发 (启动内核学习)
    解决pycharm无法使用install package
    Go 复合类型之切片类型介绍
    为什么选择事件驱动的微服务架构?
    对象模型(对象树)
    mediasoup学习与实践
  • 原文地址:https://blog.csdn.net/weixin_44162361/article/details/125448374