第58讲:Python编程中最难以理解的递归函数核心概念以及应用案例

news2024/11/19 17:48:28

文章目录

    • 1.递归函数的概念
    • 2.递归函数的使用
      • 2.1.案例一
        • 2.1.1.需求描述
        • 2.1.2.使用常规的循环来实现
        • 2.1.3.使用递归函数实现
      • 2.2.案例二
        • 2.2.1.需求描述
        • 2.2.2.使用常规的循环来实现
        • 2.2.3.使用递归函数实现
    • 3.使用递归函数计算阶乘
      • 3.1.阶乘的概念
      • 3.2.使用递归函数实现阶乘的算法
      • 3.3.以常规循环实现阶乘算法
    • 4.使用递归函数计算斐波那契数列

1.递归函数的概念

在一个函数体内,可以调用其他函数,如果在函数体内调用了该函数本身,这个函数就称为递归函数。

例如我们定义了一个fun函数,如果在fun函数体中又调用了fun函数,这就是递归函数。

递归函数包含了隐式的循环,会不停的将函数本身反复的执行,因此递归函数必须有一个明确的递归结束条件,也称为递归出口。

当没有明确的递归结束调节时,递归函数是在函数体中不停的重复调用自身函数的无限循环,就可能造成内存溢出的情况,谨慎使用,毕竟使用循环去解决问题不是稳妥的方案。

递归函数可以认为是一个死循环,会无限循环的执行函数体中的代码,因此我们必须要有一个明确的递归结束条件,否则将一直处于死循环体内,最终导致内存溢出。

使用递归函数来解决的问题必须满足两个条件:

  • 可以通过递归调用来缩小问题的规模,且新问题与原问题有相同的形式。
    • 例如我们本来要计算n的结果,但是使用递归后就可以计算n-1的结果,缩小了问题的规模
  • 可以存在一种简单的情境,可以使递归在简单情境下退出。

递归函数的优点在于定义简单,逻辑清晰。

递归最核心的思想是:每一次递归,整体问题都要比原来减小,并且递归到一定层次时,要能直接给出结果!

使用递归函数需要注意防止递归深度溢出,在Python中,通常情况下,这个深度是1000层,超过将抛出异常。在计算机中,函数递归调用是通过栈(stack)这种数据结构实现的,每当进入一个递归时,栈就会加一层,每当函数返回一次,栈就会减一层。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。

递归遍历结束后,会将每次递归的结果进行回归,例如下图是一个阶乘的递归函数用法,递推函数是n * fac(n-1),每次递归求出fac(x),如下图每次递归得到fac(6)、fac(5)、fac(4)、fac(3)、fac(2)、fac(1),最后还需要依次回归得到6 * ( 5 * ( 4 * ( 3 * ( 2 * fac(1)))))

image-20220901094537825

递归最核心的点就在于递推+回归,首先递推循环出所有可遍历的表达式,最后一次递推符合结束条件了,取到return返回值,然后使用最后一个递推得到的return返回值,依次与前一个递推进行回归,得到最终的结果。

2.递归函数的使用

2.1.案例一

2.1.1.需求描述

需求:

  • 1)调用函数时可以传入任意的字符串。
  • 2)判断传入的字符串长度是否小于5,如果小于5,则拼接一组字符串,这个字符串由(传入的实参+‘h’)组成。
  • 3)且拼接的字符串也要去判断长度是否小于5,如果小于5,则为其拼接一个’h’字符,通过循环的手段,直到拼接的字符串长度为4时为止,每次循环判断时,字符串要求是上次循环的结果。
  • 4)最后与传入的实参进行拼接,拼接时要将每次循环的结果都与实参进行拼接。
  • 5)如果一开始传入的字符串长度就大于5,则直接返回。

例如,我传入一个ha字符串,最后要给我返回出hahahhahhhahhh。

其中ha是传入的实参,hahhahhhahhh是拼接字符串,拼接字符串第一次循环判断,长度为2,小于5,那么就拼接一个h字符,得到结果为hah,第二次循环的长度为3,小于5,那么就拼接一个h字符,得到结果为hahh,第三次循环的长度为4,小于5,那么就拼接一个h字符,得到结果hahhh,第四次循环不小于5了,循环结束,将每次循环的结果都拼接在一起,最后再和传入的实参拼接,得到hahahhahhhahhh。

2.1.2.使用常规的循环来实现

实现思路:

  • 首先定义一个外层循环,判断传入的实参长度是否小于5,如果小于5,则为其拼接字符串,否则将直接返回实参值
  • 由于拼接的字符串很有特点,拼接的字符串由传入的实参+h字符组成,因此将传入的实参赋值给另外一个变量para2,后续好拼接。
  • 还需要判断拼接的字符串长度是否小于5,因此我们还要再声明一个内层循环,变量para2就是拼接的字符串,判断拼接的字符串是否小于5,如果小于5,则将para2的变量值与字符h进行拼接,然后再赋值给para2变量,这样一来,本次循环拼接的结果就定义在变量para2中了,下次循环也会按照上次循环的结果进行判断。
  • 将拼接的结果定义在para2变量中,还可以与传入的实参进行拼接,将para2变量与形参para1进行拼接,从而使得每次循环后,将得到的结果都与传递的实参进行一次拼接。
def join_5_str(para1):
    #首先定义一个循环,判断实参长度是否小于5,如果小于5,则进入循环
    while len(para1) < 5:
        #将形参para1的实参赋值给para2变量,拼接的字符串格式由传入的实参+'h'字符组成,因此可以将传入的实参先放在另外一个变量para2中
        para2 = para1
        #判断para2变量值的长度是否小于5,如果小于5,则进入循环
        while len(para2) < 5:
            #拼接字符串,将para2的变量值与字符'h'进行拼接,然后再赋值给para2变量
            para2 = para2 + 'h'
            #完成最终的字符串拼接,每次循环都会进行拼接,相当于是将每次的循环结果都进行了拼接
            #para1的变量值是传入的实参,para2的值是本次循环的拼接结果,然后将para1和para2拼接在一起,赋值给para1变量
            para1 = para1 + para2
    #如果实参的长度大于五,返回实参的值
    else:
            return para1

调用函数传入ha字符串,观察返回值。

print(join_5_str('ha'))

#输出结果:hahahhahhhahhh

image-20220823164111398

函数的执行过程分析如下:

  • 传入实参’ha’给形参para1,首先判断ha的长度是否小于5,如果小于5,则进入第一次外层循环,最外侧的循环只会循环一次,因为内层子循环完毕后,字符串的长度一定大于5。
  • 将ha赋值给para2变量,然后再判断para2拼接字符串的长度是否小于5,如果小于5,则开始第一次内层循环:
    • 开始第一次内层循环,para2的值为ha,长度为2,小于5,条件成立,将ha与h进行拼接,得到hah,然后再赋值给para2变量,再将hah与传入的实参ha进行拼接得到hahah,再赋值给para1变量。
    • 第一次内层循环时会将结果再次赋值给para2变量,因此开始第二次循内层环时,para2的值为hah,para2的长度为3,长度小于5,为hah拼接一个h,得到hahh,然后再赋值给para2变量,再将hahh与para1变量进行拼接,再赋值给para1变量,此时就会得到hahahhahh。
    • 开始第三次内层循环,思路也一样,para2的值为hahh,para2的长度为4,小于5,为hahh拼接一个h,得到hahhh,然后赋值给para2变量,再将hahhh与para1变量进行拼接,此时就会得到此时就会得到hahahhahhhahhh。
    • 第四次开始内层循环,此时para2的值为hahhh,长度大于5,循环结束。
  • 然后开始第二次外层循环,para1的值此时为hahahhahhhahhh,长度大于5,直接返回。

2.1.3.使用递归函数实现

我们的需求很有特色,你判断传入的字符串是否小于5,小于5就给拼接字符串,那你直接拼接不就好了吗,拼接的字符串来源于传入的实参,如果拼接的字符串也小于5,那么同样要进入循环判断,然后拼接一个字符h,这样一来我们就用了两次循环,如果需求更加恶心一点,要对拼接中的拼接中的拼接中的字符串长度进行判断,都要小于5,那我们岂不是要写很多循环体了。

为了避免写如下一大堆的while循环,建议大家基于这种情况去使用递归函数来实现。

image-20220823165200325

#运用递归函数
def join_5_str(para1):
    #判断实参的长度是否小于5,这也是递归函数的结束条件,只要不小于5了,递归循环就会结束
    if len(para1) < 5:
        #那么就为其拼接字符串,para1实参+递归函数join_5_str(para1 + 'h')的结果
        return para1 + join_5_str(para1 + 'h')
    else:
        return para1

#调用函数传入字符串ha
print(join_5_str('ha'))
#输出结果:hahahhahhhahhh

'''
return para1 + join_5_str(para1 + 'h')代码解释:
在这行代码中调用了join_5_str自身函数,相当于使用递归函数了,递归函数是一个隐式的循环体。
join_5_str(para1 + 'h')是递归函数的本体,递归函数的特点就是隐式循环,使用自身函数test2去处理(para1 + 'h'),第一次循环para1的值为调用join_5_str函数时传递的位置实参ha,每次都会将循环后的结果赋值给递归函数中的para1形参,依次循环,直到if len(para1) < 5条件不满足时结束递归函数的循环,拿到递归函数的返回结果,最后和函数体中的para1进行拼接。
    para1 + 'h'相当于para1 = para1 + 'h'。
    递归函数的循环过程;
        1)递归函数第一次循环:para1形参的值为ha,与h拼接后得到:para1 = ha + h,结果为hah,字符串的长度为3,满足if len(para1) < 5条件,继续进入下一次循环。
        2)递归函数第二次循环:para1形参的值为上次循环的结果为hah,与h拼接后得到:para1 = hah + h,结果为hahh,字符串的长度为4,满足if len(para1) < 5条件,继续进入下一次循环。
        3)递归函数第三次循环:para1形参的值为上次循环的结果为hahh,与h拼接后得到:para1 = hahh +h,结果为hahhh,字符串的长度为5,此时就不满足if len(para1) < 5这个条件了,递归函数循环结束。
    由于递归函数前的运算符是加法+,因此递归函数循环结束后,会将每次循环的结果相加,循环结果返回的如果是字符串就相当于字符串拼接,如果是数字就会累加求和,最终递归函数的结果就是:hah+hahh+hahhh=hahhahhhahhh。
    此时函数体中的para1的值就会和递归函数进行拼接,ha+hahhahhhahhh,得到hahahhahhhahhh。
    如果一开始para1形参接收的实参字符长度超过5了,就不会再使用递归函数拼接其他字符串,从而直接返回原字符串。
'''

上述的解释是一种特别通俗易懂的解释,不是很明白递归原理的可以先看上面的解释,当熟悉递归函数后我们也可以用递归函数的递推+回归的方式去解释:

1)首先开始递推:

  • 调用函数时传入para1的实参为ha
  • 开始第一次递推:ha + h
  • 开始第二次递推:ha + h + h
  • 开始第三次递推:ha + h +h +h,此时不符合递归条件了,结束循环。

2)开始回归:

  • 从最后一次递推的值依次向上回归。
  • 第一次回归(第二次递推的值)hahh+(第三次回归的值)hahhh
  • 第二次回归(第一次递推的值)hah+(第二次回归的值)hahhhahhh
  • 调用函数时传的实参ha+递归函数的返回值hahhhahhh,得到hahahhahhhahhh

2.2.案例二

和案例一很类似,只不过将字符串换成了数字,只为演示:递归函数循环结束后,会将每次循环的结果相加,循环结果返回的如果是字符串就相当于字符串拼接,如果是数字就会累加求和。

2.2.1.需求描述

需求:

  • 1)调用函数时可以传入任意的数字。
  • 2)判断传入的数字是否小于5,如果小于5,则加上一个数字,这个数字由(传入的实参 + 1)组成
  • 3)且加的这个数字也要去判断是否小于5,如果小于5,则为其加1,通过循环的手段,直到加的这个数字为4时为止,每次循环判断时,数字要求是上次循环的结果。
  • 4)最后与传入的实参进行相加,相加时要将每次循环的结果都与实参进行相加。
  • 5)如果一开始传入的数字实参就大于5,则直接返回。

例如,我传入一个数字1,最后返回的结果就是15。

其中1是传入的实参,14是最后返回的结果,要为实参加的数字第一次循环判断,数字为1,小于5,那么就为其加1,得到结果为1+1=2,第二次循环的数字为2,小于5,那么就为其加1,得到结果为2+1=3,第三次循环的数字为3,小于5,那么就为其加1,得到结果3+1=4,第四次循环的数字为4,小于5,那么就为其加1,得到的结果4+1=5,第五次循环的数字为5,不小于5了,循环结束,将每次循环的结果都相加在一起2+3+4+5,最后再和传入的实参相加1+2+3+4+5,得到15。

2.2.2.使用常规的循环来实现

实现思路:

  • 首先定义一个外层循环,判断传入的实参是否小于5,如果小于5,则为其加一个数字,否则将直接返回实参值。
  • 由于拼接的数字很有特点,拼接的数字由传入的实参+1组成,因此将传入的实参赋值给另外一个变量para2,后续好相加。
  • 还需要判断拼接的数字是否小于5,因此我们还要再声明一个内层循环,变量para2就是相加的数字,判断拼接的数字是否小于5,如果小于5,则将para2的变量值与1进行相加,然后再赋值给para2变量,这样一来,本次循环相加的结果就定义在变量para2中了,下次循环也会按照上次循环的结果进行判断。
  • 将相加的结果定义在para2变量中,还可以与传入的实参进行相加,将para2变量与形参para1进行相加,从而使得每次循环后,将得到的结果都与传递的实参进行一次相加。
def sum_5_int(para1):
    #首先定义一个循环,判断实参是否小于5,如果小于5,则进入循环
    while para1 < 5:
        #将形参para1的实参赋值给para2变量,相加的数字格式由传入的实参 + 1组成,因此可以将传入的实参先放在另外一个变量para2中
        para2 = para1
        #判断para2变量值的是否小于5,如果小于5,则进入循环
        while para2 < 5:
            #相加数字,将para2的变量值与1进行相加,然后再赋值给para2变量
            para2 = para2 + 1
            #完成最终的数字相加,每次循环都会进行相加,相当于是将每次的循环结果都进行了相加
            #para1的变量值是传入的实参,para2的值是本次循环的相加结果,然后将para1和para2相加,赋值给para1变量
            para1 = para1 + para2
    else:
        return para1

传入一个数字1,观察返回值。

print(sum_5_int(1))

#输出结果:15

image-20220823220043279

函数的执行过程分析如下:

  • 传入实参1给形参para1,首先判断1的是否小于5,如果小于5,则进入第一次外层循环,最外侧的循环只会循环一次,因为内层子循环完毕后,数字一定大于5。
  • 将1赋值给para2变量,然后再判断para2相加的数字是否小于5,如果小于5,则开始第一次内层循环:
    • 开始第一次内层循环,para2的值为1,小于5,条件成立,将1与1进行相加,得到1+1=2,然后再赋值给para2变量,再将2与传入的实参1进行拼接得到3,再赋值给para1变量。
    • 第一次内层循环时会将结果再次赋值给para2变量,因此开始第二次循内层环时,para2的值2,小于5,为2加1,得到2+1=3,然后再赋值给para2变量,再将3与para1变量进行相加,再赋值给para1变量,此时就会得到6。
    • 开始第三次内层循环,思路也一样,para2的值为3,小于5,为3加1,得到3+1=4,然后赋值给para2变量,再将3与para1变量进行相加,此时就会得到10。
    • 第四次开始内层循环,para2的值为4,小于5,为4加1,等到4+1=5,然后赋值给para2变量,再将5与para1变量进行相加,此时就会得到15。
    • 第五次开始内层循环,para2的值为5,不小于5,循环结束。
  • 然后开始第二次外层循环,para1的值此时为15,长度大于5,直接返回。

2.2.3.使用递归函数实现

def sum_5_int(para1):
    #判断实参的是否小于5,这也是递归函数的结束条件,只要不小于5了,递归循环就会结束
    if para1 < 5:
        #那么就为其相加一个数字,para1实参+递归函数sum_5_int(para1 + 1)的结果
        return para1 + sum_5_int(para1 + 1)
        #sum_5_int(para1 + 1)递归函数的循环结果依次为:(1+1)+(2+1)+(3+1)+(4+1)
    else:
        return para1

print(sum_5_int(1))

'''
return para1 + sum_5_int(para1 + 1)代码解释:
在这行代码中调用了sum_5_int自身函数,相当于使用递归函数了,递归函数是一个隐式的循环体。
sum_5_int(para1 + 1)是递归函数的本体,递归函数的特点就是隐式循环,使用自身函数sum_5_int去处理(para1 + 1),第一次循环para1的值为调用sum_5_int函数时传递的位置实参1,每次都会将循环后的结果赋值给递归函数中的para1形参,依次循环,直到if para1 < 5条件不满足时结束递归函数的循环,拿到递归函数的返回结果,递归函数每次循环的结果都会累加在一起,最后和函数体中的para1进行相加,并返回。
    para1 + 1相当于para1 = para1 + 1。
    递归函数的循环过程;
        1)递归函数第一次循环:para1形参的值为1,与1相加后得到:para1 = 1 + 1,结果为1+1=2,数字为2,满足if len(para1) < 5条件,继续进入下一次循环。
        2)递归函数第二次循环:para1形参的值为上次循环的结果为2,与1相加后得到:para1 = 2 + 1,结果为2+1=3,数字为3,满足if len(para1) < 5条件,继续进入下一次循环。
        3)递归函数第三次循环:para1形参的值为上次循环的结果为3,与1相加后得到:para1 = 3 + 1,结果为3+1=4,数字为4,满足if len(para1) < 5条件,继续进入下一次循环。
        4)递归函数第四次循环:para1形参的值为上次循环的结果为4,与1相加后得到:para1 = 4 + 1,结果为4+1=5,数字为5,此时就不满足if len(para1) < 5这个条件了,此时递归函数循环结束。
    由于递归函数前的运算符是加法+,因此递归函数循环结束后,会将每次循环的结果相加,循环结果返回的如果是字符串就相当于字符串拼接,如果是数字就会累加求和,最终递归函数的结果就是:1+(1+1)+(2+1)+(3+1)+(4+1)=15
    如果一开始para1形参接收的实参字符长度超过5了,就不会再使用递归函数拼接其他字符串,从而直接返回原字符串。
'''

上述的解释是一种特别通俗易懂的解释,我们也可以用递归函数的递推+回归的方式去解释:

1)首先开始递推:

  • 调用函数时传入para1的实参为1
  • 开始第一次递推:1 + 1 = 2
  • 开始第二次递推:2 + 1 = 3
  • 开始第三次递推:3 + 1 = 4
  • 开始第四次递推:4 + 1 = 5,此时不符合递归条件了,结束循环。

2)开始回归:

  • 从最后一次递推的值依次向上回归。
  • 第一次回归(第三次递推的值)4+(第四次回归的值)5 = 9
  • 第二次回归(第二次递推的值)3 +(第三次回归的值)9 = 12
  • 第三次回归(第一次递推的值)2 + (第二次回归的值)12 = 14
  • 调用函数时传的实参1+递归函数的返回值14,得到15。

3.使用递归函数计算阶乘

3.1.阶乘的概念

正整数阶乘是数学中的一种概念,也是做开发者必须要知道的数学理念。

阶乘指的是所有小于该数的正整数的积,例如我们要求正整数2的阶乘,2的阶乘就是所有小于2的正整数的积,2! = 1 * 2 = 2。或者采用递归的方式,(2-1)*2=2,结果一样。

因此阶乘的公式就是:n! = 1 * 2 * 3 * 4 * ··· * n

递归阶乘的公式为:(n-1)! * n或者是n * (n -1)!

6的阶乘:
普通阶乘的运算:
     6! = 1 * 2 * 3 * 4 * 5 *6 
        = 720
递归阶乘的运算:
	6! = (6-1)! * 6
    相当于用5的阶乘去乘以6

递归阶乘和普通阶乘的运算结果一样,只不过递归阶乘能够缩小乘积的范围,运算出来的结果也是阶乘。

0的阶乘为0,1的阶乘为1。

3.2.使用递归函数实现阶乘的算法

下面我们来定义一个递归函数来实现阶乘的算法。

def factorial(num):
    #0和1的阶乘都是本身,因此可以作为递归函数的循环结束条件
    if num == 1:
        return 1
    #采用递归阶乘的算法来实现需求:n * (n -1)!
    #定义阶乘的算法,传入的num形参值就是阶乘公式中的n,factorial(num - 1)就是(n -1)!
    #factorial(num - 1)是递归函数,会循环执行factorial函数体,直到不满足条件位置,循环结束,也就相当于是(n -1)!阶乘的运算过程
    return num * factorial(num - 1)


'''
代码解释:
	首先定义一个递归函数的循环结束体,当num的变量值等于1时,结束循环,返回1。
	如果不满足if num == 1条件,则执行num * factorial(num - 1)代码,计算传入的实参的阶乘。
	计算实参的阶乘我们通过公式n * (n -1)!作为依据,例如传入2,得到的式子就是2*(2-1)! = 2*1! = 2
	其中:n通过num形参传入,(n -1)!是计算阶乘的部分,所以通过递归函数循环去计算,然后将每次循环的结果累加在一起,最终就是传入实参的阶乘值。
'''

接下来调用factorial函数计算某个正整数的阶乘。

print(factorial(6))
print(factorial(600))

image-20220824115159452

以传入实参6位里,我们来推理函数的运行过程:

  • 1)首先判断实参6是否等于1,如果等于1则返回1,不再执行下面的代码。
  • 2)如果实参6不等于1,那么进入阶乘的算法num * factorial(num - 1),num为6,factorial(num - 1)为递归函数,递归函数需要循环去运行本函数体中的代码,直到条件不满足后,将每次循环的结果都与num进行相乘,最后得到num的阶乘。
  • 3)factorial(num - 1)递归函数循环过程:
    • 1、第一次递归函数的循环:num形参为6,减1之后得到num = 6 - 1,结果为6-1=5,num为5,不满足if num == 1条件,进入下一次循环。
    • 2、第二次递归函数的循环:num形参为5,减1之后得到num = 5 - 1,结果为5-1=4,num为4,不满足if num == 1条件,进入下一次循环。
    • 3、第三次递归函数的循环:num形参为4,减1之后得到num = 4 - 1,结果为4-1=4,num为3,不满足if num == 1条件,进入下一次循环。
    • 4、第四次递归函数的循环:num形参为3,减1之后得到num = 3 - 1,结果为3-1=2,num为2,不满足if num == 1条件,进入下一次循环。
    • 5、第三次递归函数的循环:num形参为2,减1之后得到num = 2 - 1,结果为2-1=1,num为1,满足if num == 1条件,递归函数循环结束。
  • 4)由于递归函数前的运算符是乘法*,因此递归函数循环结束后,会将每次循环的结果相乘,最终递归函数的结果就是5*4*3*2*1
  • 5)最后再与传入的实参6进行相乘6*5*4*3*2*1=720,那么实参6的阶乘就是720。

上述的解释是一种特别通俗易懂的解释,我们也可以用递归函数的递推+回归的方式去解释:

1)首先开始递推:

  • 调用函数时传入para1的实参为6
  • 开始第一次递推:factorial(6 - 1) = factorial(5)
  • 开始第二次递推:factorial(5 - 1) = factorial(4)
  • 开始第三次递推:factorial(4 - 1) = factorial(3)
  • 开始第四次递推:factorial(3 - 1) = factorial(2)
  • 开始第五次递推:factorial(2 - 1) = factorial(1),此时不符合递归条件了,结束循环。

2)开始回归:

  • 最后一次递推得到的return返回值为1,从最后一次递推的值依次向上回归,本次递归使用的运算符是乘,因此回归的时候也是乘法。
  • 第一次回归:(第五次递推的值)1 * (return返回值)1 = 1
  • 第二次回归:(第四次递推的值)2 * (第五次回归的值)1 = 2
  • 第三次回归:(第三次递推的值)3 * (第四次回归的值)2 = 6
  • 第四次回归:(第二次递推的值)4 * (第三次回归的值)6 = 24
  • 第五次回归:(第一次递归的值)5 * (第二次回归的值)24 = 120
  • 到此递归函数已经拿到返回值120了,与传入的实参相乘即6*120=600
  • 或者可以看成回归是这样的:return 6*120*24*6*2*1

在这里插入图片描述

3.3.以常规循环实现阶乘算法

def factorial(num):
    #判断传入的参数是否不等于1
    while num != 1:
        #将传入的实参赋值给numfac变量,用于做阶乘(n-1)!公式
        numfac = num
        #如果numfac的值不为1,则开始循环
        while numfac != 1:
            #每次循环都将numfac的值减1,然后再赋值给numfac变量,直到numfac=1时,退出循环,这里对应的阶乘公式为(n-1)!
            numfac = numfac - 1
            #每次循环时都将numfac与num形参相乘,这里对应阶乘的公式n*(n-1)!
            num = num * numfac
        #循环完后,实参的阶乘就计算完毕了,返回
        return num
    #如果等于1,则直接返回1
    else:
         return 1

print(factorial(6))
print(factorial(600))

image-20220824115159452

以传入实参6位里,我们来推理函数的运行过程:

  • 1)首先判断实参6是否不等于1,如果不等于1则执行外层循环体,否则将直接返回1。

  • 2)外层循环开始第一次循环,将传入的实参6赋值给numfac变量,然后开始执行内层循环体:

    • 1、开始第一次内层循环,numfac值为6,不等于1,条件成立,将numfac的值减1,也就是6-1=5,此时numfac的值为5,num形参对应的实参为6,然后将实参6与numfac的值5相乘,再赋值给num变量。

    • 2、开始第二次内层循环,numfac此时的值为5,不等于1,条件成立,将numfac的值减1,也就是5-1=4,此时numfac的值为4,num变量值经过上次计此时为30,然后将num变量值30与numfac的值4相乘,再赋值给num变量。

    • 3、开始第三次内层循环,numfac此时的值为4,不等于1,条件成立,将numfac的值减1,也就是4-1=3,此时numfac的值为3,num变量值经过上次计此时为120,然后将num变量值120与numfac的值3相乘,再赋值给num变量。

    • 4、开始第四次内层循环,numfac此时的值为3,不等于1,条件成立,将numfac的值减1,也就是3-1=2,此时numfac的值为2,num变量值经过上次计此时为360,然后将num变量值360与numfac的值2相乘,再赋值给num变量。

    • 5、开始第五次内层循环,numfac此时的值为2,不等于1,条件成立,将numfac的值减1,也就是2-1=1,此时numfac的值为1,num变量值经过上次计此时为720,然后将num变量值720与numfac的值1相乘,再赋值给num变量。

    • 6、开始第六次内层循环,numfac地址的值为1,1等于1,条件不成立,内层循环结束。

  • 3)执行完内层循环体之后,直接返回此时num变量的值,这个值就是实参阶乘的结果,不能使外层循环再循环一次,否则算法就不对了。

4.使用递归函数计算斐波那契数列

斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……在数学上,斐波那契数列以如下被以递推的方法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)在现代物理、准晶体结构、化学等领域

下面使用递归函数实现斐波那契数列的算法。

def fibonacci(n):
    """使用递归函数计算斐波那契数列"""
    if n == 0:
        return 0
    if n == 1:
        return 1
    return fibonacci(n - 1) + fibonacci(n - 2)

我们使用fibonacci()函数来查询整数6的斐波那契数列。

print(fibonacci(6))

#输出结果8。

为什么会输出8呢?因为我输入的整数是6,在斐波那契数列中找到前6个数字,将最后两个数字相加即为8。

我们可以通过以下方法去打印出对应的斐波那契数列。

num = int(input("请输入一个数字:"))
if num <= 0:
    print('请输入一个正数')
else:
    for i in range(num):
        print(fibonacci(i))
#print(fibonacci(6))

image-20220824142334268

斐波那契数列递归流程图:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/641239.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

分布式Profinet IO模块

PROFINET IO模块是一种用于工业自动化控制系统中的设备控制模块。它使用以太网技术&#xff0c;在现场设备和处理器/控制器之间提供快速、精确和可靠的数据交换。PROFINET IO模块通常是面向过程的&#xff0c;可以用于监测和控制工业过程中的各种设备和参数&#xff0c;如传感器…

Vue中使用editor.md(2):添加拖拽图片上传功能

0. 背景 在对editor.md简单使用后&#xff0c;希望添加图片拖拽或粘贴上传的功能。 简单使用参考&#xff1a;Vue中使用editor.md&#xff08;1&#xff09;&#xff1a;简单使用 1. 实现 1.1 添加粘贴监听 // 使用axios import axios from /api/indexfunction initPasteDra…

什么是Vue的插件?如编写自定义 Plugin?

什么是Vue的插件&#xff1f; 在Vue开发中&#xff0c;我们经常需要使用一些第三方库或功能性模块&#xff0c;Vue插件就是一种将这些库或模块集成到Vue应用中的方式。插件是Vue.js提供的一种机制&#xff0c;用于扩展Vue的功能。插件通常用于封装某些特定的功能&#xff0c;例…

【AI人工智能】 你如果要使用最强大的语言模型,你还要有最精美的浏览器标签页iTab (2)

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享博主 &#x1f40b; 希望大家多多支持一下, 我们一起进步&#xff01;&#x1f604; &#x1f3c5; 如果文章对你有帮助的话&#xff0c;欢迎评论 &#x1f4ac;点赞&#x1…

UnityA*导航算法,通俗易懂

首先A*寻路算法分为2D算法和3D算法&#xff0c;目前它甚至不如NAVMesh算法效率高。但NAVMesh不适用于2D&#xff0c;因此2D还是要靠A*算法来进行实现。 当前就来说说2D是如何实现的。 首先2DA*算法先要将地图划分成格子分块标记成二维数组 每个格子依据x&#xff0c;y&#xf…

又一新型技术全面铺开,高精度光刻机已突破壁垒,赶超只是时间

众所周知&#xff0c;光刻机是制造高精度芯片的关键步骤。 随着科技的不断进步&#xff0c;光刻机的精度和速度也在不断提高&#xff0c;使得我们可以制造出更小更精细的芯片&#xff0c;满足了人们对于高性能电子设备的需求。 我国的光刻机技术&#xff0c;国产大飞机&#x…

【华为OD机试真题2023B卷 JAVAJS】二维伞的雨滴效应

华为OD2023(B卷)机试题库全覆盖,刷题指南点这里 二维伞的雨滴效应 知识点递归树 时间限制:1s 空间限制:256MB 限定语言:不限 题目描述: 普通的伞在二维平面世界中,左右两侧均有一条边,而两侧伞边最下面各有一个伞坠子,雨滴落到伞面,逐步流到伞坠处,会将伞坠的信息携…

UML类图(二)

相信希望&#xff0c;相信自己 上一章简单介绍了 设计模式的原则(一), 如果没有看过,请观看上一章 本文参考文章: 常见UML符号详解 UML (Unified modeling language) 统一建模语言&#xff0c;是一种用于软件系统分析和设计的语言工具&#xff0c; 它用于帮助软件开发人员进行…

强烈推荐协同办公的三个小工具

强烈推荐协同办公的三个小工具 这里给大家推荐三个可以用于协同办公的小软件工具。那么&#xff0c;什么是协同办公呢&#xff1f;直接下定义给个概念那就不好玩了&#xff0c;所以我直接推荐这么几个协同软件大家就懂了。 ONLYOFFICE办公软件 具体的软件截图我这里就不贴了&…

【Android】Frida Hook 文件读写操作

前言 在挖掘客户端漏洞的时候&#xff0c;通常会关注应用对什么文件进行了读写操作&#xff0c;当我们能控制被读的文件或观测到敏感写入的文件&#xff0c;通常可以造成一定危害。本文详细介绍了如何通过frida监控文件读写操作。 相关知识 1. 读写相关调用api 在Linux系统…

有哪些自媒体平台有播放量就会有收益?

自媒体是近年来兴起的一种新型媒体&#xff0c;指的是由个人或小团体通过互联网自行发布内容&#xff0c;通过社交媒体等渠道传播和推广的媒体形态。自媒体平台的出现&#xff0c;让更多人能够自由发表意见和观点&#xff0c;实现了信息的自由传播。同时&#xff0c;随着互联网…

跨平台.NET应用UI组件DevExpress XAF v22.2亮点 - 支持.NET 7

DevExpress XAF是一款强大的现代应用程序框架&#xff0c;允许同时开发ASP.NET和WinForms。DevExpress XAF采用模块化设计&#xff0c;开发人员可以选择内建模块&#xff0c;也可以自行创建&#xff0c;从而以更快的速度和比开发人员当前更强有力的方式创建应用程序。 在新版中…

数据结构——二叉树基础结构篇(C语言)

引言 现在是北京时间2023年6月13日9点11分。从决定要开始减脂之后&#xff0c;饥饿总是伴随着我。一觉起来肚子咕咕叫&#xff0c;我还是想先把文章发了再吃第一餐。燕麦加蛋白粉几乎伴随了我大学的第一年早饭。昨天练了一个小时背&#xff0c;练背后还做了45分钟有氧。空腹训…

BeautifulPrompt:PAI推出自研Prompt美化器,赋能AIGC一键出美图

作者&#xff1a;曹庭锋、汪诚愚、吴梓恒、黄俊 背景 Stable Diffusion&#xff08;SD&#xff09;是一种流行的AI生成内容&#xff08;AI Generated Content&#xff0c;AIGC&#xff09;模型&#xff0c;能在文字输入的基础上生成各种风格多样的图像。在目前的AIGC方向&…

十五周算法训练营——普通动态规划(上)

今天是十五周算法训练营的第十一周&#xff0c;主要讲普通动态规划&#xff08;上&#xff09;专题。&#xff08;欢迎加入十五周算法训练营&#xff0c;与小伙伴一起卷算法&#xff09; 斐波那契数 斐波那契数 &#xff08;通常用 F(n) 表示&#xff09;形成的序列称为 斐波那…

设计模式——适配器模式

1.定义 将一个类的接口转换成客户所希望的另一个接口&#xff0c;Adapter模式使得那些原本因为接口不兼容而不能一起工作的那些类可以一起工作。 2.使用场景 一般来说&#xff0c;适配器模式可以看作一种“补偿模式”&#xff0c;用来补救设计上的缺陷。应用这种模式算是“无…

驱动开发:内核LoadLibrary实现DLL注入

远程线程注入是最常用的一种注入技术&#xff0c;在应用层注入是通过CreateRemoteThread这个函数实现的&#xff0c;该函数通过创建线程并调用 LoadLibrary 动态载入指定的DLL来实现注入&#xff0c;而在内核层同样存在一个类似的内核函数RtlCreateUserThread&#xff0c;但需要…

【模型评估】AP 和他们的兄弟们:mAP、AP50、APs、APm、APl

AP是在目标检测任务中&#xff0c;尝尝被用于评估模型预测能力的指标。那AP是什么&#xff1f;为什么能够充当不同模型综合对比评测的公认指标呢&#xff1f; 在学习下文之前&#xff0c;混淆矩阵和ROC可以先了解下&#xff1a; 【模型评估】混淆矩阵&#xff08;confusion_m…

世界中西医结合医学研究院一行莅临万民健康交流指导

为进一步发展中医药产业&#xff0c;深入挖掘中医药文化&#xff0c;坚持中西医并重&#xff0c;传承精华&#xff0c;守正创新&#xff0c;助力乡村振兴、促进乡村医疗产业发展。6 月 10 日 &#xff0c; 世界中西医结合医学研究院医学工程院院士罗先义 、谈家桢生命基金会主任…

测试左移及其相关实践

本文首发于个人网站「BY林子」&#xff0c;转载请参考版权声明。 之前在《敏捷测试的核心》、《构建测试的体系化思维&#xff08;进阶篇&#xff09;》和《一页纸测试策略》等文章中提到过测试左移&#xff0c;但是没有专门针对这个主题做过系统的介绍&#xff0c;但又总是被社…