码迷,mamicode.com
首页 > 编程语言 > 详细

python递归函数

时间:2020-07-26 00:35:10      阅读:72      评论:0      收藏:0      [点我收藏+]

标签:是你   关系   font   def   路径   使用   函数调用   现在   ror   

递归函数
# 了解什么事递归 :在函数中调用自身就是递归函数
# 最大递归深度默认的最大深度为997 / 997 是python从内存角度出发做得限制
# 能知道递归
# 能知道递归的应用场景
# 初始递归
# 算法--二分查找算法
# 三级菜单--递归实现
# RecursionError: maximum recursion depth exceeded while calling a Python object
# 递归的错误,超过了递归的最大深度
# 以下可以设置递归的深度
# import sys
# sys.setrecursionlimit() -->不能随便修改
# 如果递归次数太多,就不合适使用递归函数解决问题了
# 缺点:占内存
# 优点:会让代码变简单

楔子

在讲今天的内容之前,我们先来讲一个故事,讲的什么呢?从前有座山,山里有座庙,庙里有个老和尚讲故事,讲的什么呢?从前有座山,山里有座庙,庙里有个老和尚讲故事,讲的什么呢?从前有座山,山里有座庙,庙里有个老和尚讲故事,讲的什么呢?从前有座山,山里有座庙,庙里有个老和尚讲故事,讲的什么呢......这个故事你们不喊停我能讲一天!我们说,生活中的例子也能被写成程序,刚刚这个故事,让你们写,你们怎么写呀?

while True:
    story = "
    从前有个山,山里有座庙,庙里老和尚讲故事,
    讲的什么呢?  
    "
    print(story)

你肯定是要这么写的,但是,现在我们已经学了函数了,什么东西都要放到函数里去调用、执行。于是你肯定会说,我就这么写

def story():
    s = """
    从前有个山,山里有座庙,庙里老和尚讲故事,
    讲的什么呢?
    """
    print(s)
   
while True:
    story()

但是大家来看看,我是怎么写的!

def story():
    s = """
    从前有个山,山里有座庙,庙里老和尚讲故事,
    讲的什么呢?
    """
    print(s)
    story()
   
story()

先不管函数最后的报错,除了报错之外,我们能看的出来,这一段代码和上面的代码执行效果是一样的。

递归的定义——在一个函数里再调用这个函数本身

现在我们已经大概知道刚刚讲的story函数做了什么,就是在一个函数里再调用这个函数本身,这种魔性的使用函数的方式就叫做递归

刚刚我们就已经写了一个最简单的递归函数。

递归的最大深度——997

正如你们刚刚看到的,递归函数如果不受到外力的阻止会一直执行下去。但是我们之前已经说过关于函数调用的问题,每一次函数调用都会产生一个属于它自己的名称空间,如果一直调用下去,就会造成名称空间占用太多内存的问题,于是python为了杜绝此类现象,强制的将递归层数控制在了997(只要997!你买不了吃亏,买不了上当...).

拿什么来证明这个“997理论”呢?这里我们可以做一个实验:

def foo(n):
    print(n)
    n += 1
    foo(n)
foo(1)

此我们可以看出,未报错之前能看到的最大数字就是997.当然了,997是python为了我们程序的内存优化所设定的一个默认值,我们当然还可以通过一些手段去修改它:

import sys
print(sys.setrecursionlimit(100000))

我们可以通过这种方式来修改递归的最大深度,刚刚我们将python允许的递归深度设置为了10w,至于实际可以达到的深度就取决于计算机的性能了。不过我们还是不推荐修改这个默认的递归深度,因为如果用997层递归都没有解决的问题要么是不适合使用递归来解决要么是你代码写的太烂了~~~

看到这里,你可能会觉得递归也并不是多么好的东西,不如while True好用呢!然而,江湖上流传这这样一句话叫做:人理解循环,神理解递归。所以你可别小看了递归函数,很多人被拦在大神的门槛外这么多年,就是因为没能领悟递归的真谛。而且之后我们学习的很多算法都会和递归有关系。来吧,只有学会了才有资本嫌弃!

再谈递归

例子

# 问我,小红多大了,我不告诉你,但告诉你我比小明大2岁   n =1   age(1) = age(2) + 2 = age(n+1)+2 = 24
# 你去问小明,小明不告诉你,但告诉你我比小李大2岁 n =2 age(2) = age(3)+2 = age(n+1)+2 = 22
# 你是不是还得去问小李,小李也不告诉你,但我比小武大2岁 n =3 age(3) = age(4)+2 = age(n+1)+2 =20
# 你是不是还得去问小武,小武今年 18 n =4 age4 = 18

# n = 4 age(4) = 18
# n < 4 age(n) = age(n+1) +2
def age(n):
if n==4:
return 18
elif n> 0 and n< 4:
return age(n+1) +2

print(age(1))
# 教你看递归
def age(1): # 所以最后返回了24
if 1==4:
return 18
elif 1> 0 and 1< 4:
return age(1+1) +2 # age(1+1) 递->发生了函数调用还没执行返回和+2
# 归->这里返回调用age(2)的结果 22 相对于 22 +2 = 24

def age(2):
if 2==4:
return 18
elif 2> 0 and 2< 4:
return age(2+1) +2 # # age(2+1) 递->发生了函数调用还没执行返回和+2
# 归->这里返回调用age(3)的结果 20 相对于 20 +2 = 22

def age(3):
if 3==4:
return 18
elif 3> 0 and 3< 4:
return age(3+1) +2 # # age(3+1) 递->发生了函数调用还没执行返回和+2
# 归->这里调用age(4)的返回结果 18 相对于 18 +2 = 20
def age(4):
if 4==4: # 等于4时走if条件 函数调用结束
return 18
elif 4> 0 and 4< 4:
return age(4+1) +2 #

# 什么叫算法
# 计算的方法:人脑复杂计算机简单

# 99 * 13 = 1287= 13 * 100 -3
# 查找:找数据
# 排序
# 最短路径
# 我们学习的算法,都是过去时,了解基础的算法,才能创造出更好的算法
# 不是所有的事情都能套用现成的方法解决的,有些时候会用到学过的算法知识来解决新的问题

# 二分查找法必须处理有序的的列表,现在index不让用,创造简单的算法实现寻找下标
# l = [2,3,5,10,15,16,18,22,26,30,32,35,41,42,43,55,56,66,67,69,72,76,82,83,88]

你说,so easy!

l.index(66)...

我们之所以用index方法可以找到,是因为python帮我们实现了查找方法。如果,index方法不给你用了。。。你还能找到这个66的索引么?

i = 0 for num in l:
    if num == 66:
        print(i)
    i+=1

上面这个方法就实现了从一个列表中找到66所在的位置了。

但我们现在是怎么找到这个数的呀?是不是循环这个列表,一个一个的找的呀?假如我们这个列表特别长,里面好好几十万个数,那我们找一个数如果运气不好的话是不是要对比十几万次?这样效率太低了,我们得想一个新办法。

二分查找算法

l = [2,3,5,10,15,16,18,22,26,30,32,35,41,42,43,55,56,66,67,69,72,76,82,83,88]

你观察这个列表,这是不是一个从小到大排序的有序列表呀?

如果这样,假如我要找的数比列表中间的数还大,是不是我直接在列表的后半边找就行了?

技术图片

 

这就是二分查找算法

那么落实到代码上我们应该怎么实现呢? 

简单版二分法

# 切片寻找,但是寻找到的下标与原列表下标不一致
# def find_index(l, aim):
# indexs = len(l) // 2 # 两个杠整除
# if l[indexs] < aim:
# new_1= l[indexs+1:]
# find_index(new_1,aim)
# elif l[indexs] > aim:
# new_1= l[:indexs]
# find_index(new_1, aim)
# else:
# print(‘找到了‘,indexs,l[indexs])
# find_index(l,66)

升级版二分法
l = [2,3,5,10,15,16,18,22,26,30,32,35,41,42,43,55,56,66,67,69,72,76,82,83,88]   # 12 24
def find(l,aim,start=0,end = None):
end = len(l) if end is None else end # 第一轮 end = len(l)= 24
indexs = (end - start) // 2 + start # 第一轮(25-0)//2 + 0 计算中间值12
if l[indexs] < aim: # l[12] < 44 # 41 <44
find(l,aim,start = indexs + 1,end = end) # find(l,44,start=13,end=24)
elif l[indexs] > aim:
find(l,aim,start = start,end = indexs-1)
else:
print(‘找到了‘,indexs,aim)

def find(l,aim,start=0,end = None): # start=13,end=24
end = len(l) if end is None else end # 第二轮 end = 24
indexs = (end - start) // 2 + start # 第二轮(23-13)//2 + 13 计算中间值18
if l[indexs] < aim:
find(l,aim,start = indexs + 1,end = end)
elif l[indexs] > aim: # l[18] > 44 # 67 > 44
find(l,aim,start = start,end = indexs-1) # find(l,44,start=13,end=18 -1 =17)
else:
print(‘找到了‘,indexs,aim)

def find(l,aim,start=0,end = None): #start=13,end=17
end = len(l) if end is None else end # 第三轮 end = 17
indexs = (end - start) // 2 + start # 第三轮(17-13)//2 + 15 计算中间值18
if l[indexs] < aim:
find(l,aim,start = indexs + 1,end = end)
elif l[indexs] > aim: # l[15] > 44 # 55 > 44
find(l,aim,start = start,end = indexs-1) # find(l,44,start=13,end=15 -1 =14)
else:
print(‘找到了‘,indexs,aim)

def find(l,aim,start=0,end = None): #start=13,end=14
end = len(l) if end is None else end # 第四轮 end = 14
indexs = (end - start) // 2 + start # 第四轮(14-13)//2 + 13 计算中间值13
if l[indexs] < aim: # l[13] > 44 # 42 > 44
find(l,aim,start = indexs + 1,end = end) # find(l,44,start=14,end=15 -1 =14)
elif l[indexs] > aim:
find(l,aim,start = start,end = indexs-1)
else:
print(‘找到了‘,indexs,aim)

def find(l,aim,start=0,end = None): #start=14,end=14
end = len(l) if end is None else end # 第五轮 end = 14
indexs = (end - start) // 2 + start # 第五轮(14-14)//2 + 14 计算中间值14
if start <= end :
if l[indexs] < aim:
return find(l,aim,start = indexs + 1,end = end)
elif l[indexs] > aim: # l[14] > 44 # 43 < 44
return find(l,aim,start = start,end = indexs-1) # find(l,44,start=15,end=14)
else:
return ‘找到了,索引值为:‘,indexs,aim
else:
return ‘找不到该值‘

ret =find(l,44)
print(ret)

# 超出了值范围不行,比如100
# 以下分别找
# 67 发生两次调用
# 66 发生好几次
# 44 最后找不到了

递归函数作业
超过最大递归限制的报错
# 只要写递归函数,必须要有结束条件
#
# 返回值,不要只看到return就认为已经返回了,要看返回操作是在递归到第几层的时候发生的,
# 然后返回给了谁,如果不是返回给最外层函数,调用者就接收不到
# 需要再分析,看如何把结果返回回来
#
# 循环 --->递归
# 算法和递归相关

# 斐波那契数序列
# 1,1,2,3,5,8 13 # fib(6) = fib(5)+ fib(4)
# fib(5) = fib(4)+ fib(3)
# fib(4) = fib(3)+ fib(2)
# fib(3) = fib(2)+ fib(1)
# fib(2) = 1
# fib(1) = 1

# def fib(n):
# if n == 1 or n ==2:
# return 1
# return (fib(n-1) + fib(n -2)) -->避免用双递归
# 传入3是的情况
# def fib(3): # 返回的结果等于2
# if n == 1 or n ==2:
# return 1
# return (fib(3-1) + fib(3 -2)) # 停在这里
#这里调用自己传入参数2返回1 + 调用自己传入参数1返回值1 两个返回的结构相加等于2

#以下进阶方法,就是传入一个值后,一次性返回两个值
def fib(n,l = [0]):
l[0] += 1
if n == 1 or n == 2:
l[0] -= 1
return 1,1
else:
a,b = fib(n-1) # 停在这里 2
l[0] -= 1
if l[0] == 0:
return a+b
return b,a+b


print(fib(4)) #传入参数5
def fib(4,l = [0]):
l[0] += 1
if 4 == 1 or 4 == 2:
l[0] -= 1
return 1,1
else:
a,b = fib(4-1) # 1,2
l[0] -= 1
if l[0] == 0:
return a+b
return b,a+b # 2,1+2 = 2,3
#
def fib(3,l = [0]):
l[0] += 1
if 3 == 1 or 3 == 2:
l[0] -= 1
return 1,1
else:
a,b = fib(3-1) # 1,1
l[0] -= 1
if l[0] == 0:
return a+b
return b,a+b # 1,1+1 = 1,2

def fib(2,l = [0]):
l[0] += 1
if 3 == 1 or 3 == 2:
l[0] -= 1
return 1,1
else:
a,b = fib(3-1) # 停在这里 2
l[0] -= 1
if l[0] == 0:
return a+b
return b,a+b

# a = 2
# b = 3
# a = 3
# b = 2+3
# a = 5
# b = 3+5
# a = 8
# b = 13

# 阶乘
# 3! 3*2*1
# 2! 2*1
# 1! 1
# def fac(n):
# if n == 1:
# return 1
# return n * fac(n-1)
# print(fac(20))
# 附加题

递归函数与三级菜单


 

python递归函数

标签:是你   关系   font   def   路径   使用   函数调用   现在   ror   

原文地址:https://www.cnblogs.com/tinaLi/p/13347237.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!