Python进阶-Ⅸ 递归 二分法

1、算法

英文名:algorithm,就是计算的方法。
# 是截止到目前,人类发现的针对特定场景的,最优的计算方法。是人类智慧的结晶。
# 人脑是复杂的,电脑其实很简单。比如:

999 * 123 人类会将其变为: 1000 * 123 - 123 这样就好算多了,可是电脑不会如此,只会硬算!
学习算法的目的
# 我们学习的算法 都是过去时
# 了解基础的算法 才能创造出更好的算法
# 不是所有的事情都能套用现成的方法解决的
# 有些时候会用到学过的算法知识来解决新的问题

2、递归

1)、楔子

有如下例子:
从前有座山,山上有个庙;庙里有两个和尚,一个老和尚跟一个小和尚。一天,老和尚跟小和尚讲故事:
"从前有座山,山上有个庙;庙里有两个和尚,一个老和尚跟一个小和尚。一天,老和尚跟小和尚讲故事:
'从前有座山,山上有个庙;庙里有两个和尚,一个老和尚跟一个小和尚。一天,老和尚跟小和尚讲故事:
.............................
# 看到这个例子,有何感想?这不是车轱辘话码,自己说自己!
# 这就对了,我们就正式引入递归!
2)、递归函数的定义
在函数中,自己调用自己的函数,叫递归函数。
 1 depth = 0
 2 def temple_story():
 3     global depth
 4     print('从前有座山,山上有个庙;庙里有两个和尚,一个老和尚跟一个小和尚。一天,老和尚跟小和尚讲故事:')
 5     depth += 1
 6     print(depth)
 7     temple_story()
 8 
 9 #temple_story() #会报告超过最大递归深度的错误!
10 # 报错:RecursionError: maximum recursion depth exceeded while calling a Python object
11 
12 # 我们来看看这个深度是多少?添加计数,发现是997次!
13 # 这个最大递归次数是python定义的,可以改,但建议如此做,因为递归如此多,还解决不了,就说明不适合用递归解决!
14 import sys
15 print("最大递归深度是:", sys.getrecursionlimit())  # 1000
16 sys.setrecursionlimit(50000)
17 
18 #temple_story()  # 没有报告错误!执行到3806次就没打印了(并且退出了函数),说明有东西限制了继续递归!
3)、递归的小结
# 如果递归次数太多,就不适合使用递归来解决问题
# 递归的缺点 : 占内存
# 递归的优点: 会让代码变简单
4)、应用场景1:询问年龄
 1 '''
 2 # alex 多大       n = 1   age(1) = age(2)+2 = age(n+1) + 2
 3 # alex比egon大两岁
 4 # egon多大?      n = 2   age(2) = age(3) + 2 = age(n+1) +2
 5 # egon比tom大两岁
 6 # tom多大       n = 3   age(3) = age(4) + 2 = age(n+1) +2
 7 # tom比king大两岁
 8 # king多大?
 9 # king40了      n = 4   age(4) = 40
10 '''
11 def ask_age(n):
12     '''
13     问年龄,
14     :param n:被询问的序号
15     :return: age
16     '''
17     if n == 4:
18         return 40
19     elif 0 < n < 4:
20         return ask_age(n + 1) + 2
21 print('I am ', ask_age(3))
22 
23 # # 教你看递归  递的是n,归的是return的值
24 # def ask_age(1):
25 #     if 1 == 4:
26 #         return 40
27 #     elif 1 > 0 and 1 < 4:
28 #         return 46
29 #
30 # def ask_age(2):
31 #     if 2 == 4:
32 #         return 40
33 #     elif 2 >0 and 2 < 4:
34 #         age(3) + 2    None +2
35 #
36 # def ask_age(3):
37 #     if 3 == 4:
38 #         return 40
39 #     elif 3 >0 and 3 < 4:
40 #         42
41 #
42 # def ask_age(4):
43 #     if 4 == 4:
44 #         return 40
45 #     elif n >0 and n < 4:
46 #         age(n+1) + 2
View Code

 

3、二分法 (dichotomy),必须处理有序的列表

  1 #   使用实例:使用二分法查找数列中有没有66,并返回其索引。
  2 num_li = [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]
  3 
  4 # 没接触二分法之前,我们大概会用for循环遍历数列,判断其中元素是否是66,如果是返回其索引!
  5 # 但是,想一想,如果此数列有几百万个元素,这样遍历会很慢,如何查找最快?答案是:二分法
  6 # 第一次,找到数列最中间的元素,比较它和66的大小,如果大于66,就说明66在它前面,前面的数又组成一个数列num_li_new_1,
  7 # 如果它比66小,则说明66在它的后面,将其后面的数,组成一个新的数列:num_li_new_2
  8 # 第二次,在num_li_new_1或者num_li_new_2中,继续按照第一的方法寻找下去。。。。。。
  9 # 第n次,如果有该元素,一定会找到它,而且只有一个元素了!
 10 
 11 #1)、第一次尝试用二分法
 12 def find_num(list, target):
 13     mid_index = len(list)//2
 14     if list[mid_index] == target:
 15         print('Congratunation! You get it!')
 16         return mid_index
 17     elif list[mid_index] > target:
 18         new_list = list[:mid_index]
 19         find_num(new_list, target)
 20     else:
 21         new_list = list[mid_index + 1:]
 22         find_num(new_list, target)
 23 
 24 res = find_num(num_li, 67)
 25 print(res)
 26 
 27 # 惊奇的发现,打印的结果是:None
 28 # 分析未得到预想结果的原因: todo:没有接收返回值,也没有返回任何值! 所以为None
 29 def find_num(list, target):  # 第一步:list=num_li target = 67
 30     mid_index = len(list) // 2   # 第二步:mid_index = 24//2 = 12
 31     if list[mid_index] == target:   # list[mid_index] = list[12] = 41
 32         print('Congratunation! You get it!')
 33         return mid_index
 34     elif list[ mid_index ] > target:
 35         new_list = list[ :mid_index ]
 36         find_num(new_list, target)
 37     else:                           #第三步:41 < 67
 38         new_list = list[ mid_index + 1: ] # 第四步:new_list = list[13:] = [42,43,55,56,66,67,69,72,76,82,83,88]
 39         find_num(new_list, target)  # 第五步:find_num(new_list, 67)todo:没有接收返回值,也没有返回任何值!
 40 
 41 def find_num(list, target):  # 第六步:list=new_list target = 67
 42     mid_index = len(list) // 2   # 第七步:mid_index = 12//2 = 6
 43     if list[mid_index] == target:   # list[mid_index] = list[6] = 69
 44         print('Congratunation! You get it!')
 45         return mid_index
 46     elif list[ mid_index ] > target:  # 第八步:69 > 67
 47         new_list = list[ :mid_index ]  # 第九步:new_list = list[:6] = [42,43,55,56,66,67]
 48         find_num(new_list, target)  # 第十步:find_num(new_list, 67) todo:没有接收返回值,也没有返回任何值!
 49     else:
 50         new_list = list[ mid_index + 1: ] #
 51         find_num(new_list, target)  #
 52 
 53 def find_num(list, target):  # 第十一步:list=new_list target = 67
 54     mid_index = len(list) // 2   # 第十二步:mid_index = 6//2 = 3
 55     if list[mid_index] == target:   # list[mid_index] = list[3] = 56
 56         print('Congratunation! You get it!')
 57         return mid_index
 58     elif list[ mid_index ] > target:  #
 59         new_list = list[ :mid_index ]
 60         find_num(new_list, target)  # 第十步:find_num(new_list, 67)
 61     else:                           # 第十三步:56 < 67
 62         new_list = list[mid_index + 1:]  # 第十四步:new_list = list[4:] = [66,67]
 63         find_num(new_list, target)  # 第十五步:  find_num(new_list, 67) todo:没有接收返回值,也没有返回任何值!
 64 
 65 def find_num(list, target):  # 第十六步: list=new_list target = 67
 66     mid_index = len(list)//2  # 第十七步:mid_index = 2//2 = 1
 67     if list[mid_index] == target:  # 第十八步:list[mid_index] = list[1] = 67 正好相等!
 68         print('Congratunation! You get it!') # 第十九步:输出Congratunation! You get it!
 69         return mid_index            # 第二十步:将mid_index = 1 todo 返回给调用该函数的地方:即第十五步
 70     elif list[mid_index] > target:
 71         new_list = list[:mid_index]
 72         find_num(new_list, target)
 73     else:
 74         new_list = list[mid_index + 1:]
 75         find_num(new_list, target)
 76 
 77 #2)、第二次尝试用二分法
 78 
 79 def find_num(list, target):
 80     mid_index = len(list)//2
 81     if list[mid_index] == target:
 82         print('Congratunation! You get it!')
 83         return mid_index
 84     elif list[mid_index] > target:
 85         new_list = list[:mid_index]
 86         return find_num(new_list, target)
 87     else:
 88         new_list = list[mid_index + 1:]
 89         return find_num(new_list, target)
 90 
 91 res = find_num(num_li, 67)
 92 print(res)
 93 
 94 # 继续惊奇的发现,结果是:0,这并不是我们预想的结果!
 95 # 原因分析:索引乱了,我们切分数列后,用的是新的索引!
 96 
 97 # 3)、第三次尝试用二分法
 98 
 99 #def find_num(list, target, start = 0, end = len(list)): #TypeError: object of type 'type' has no len()
100 def find_num(list, target, start = 0, end = None):
101     end = len(list) if end is None else end
102    # mid_index = len(list)//2   # 有时会报错:IndexError: list index out of range
103     mid_index = (end - start)//2 + start   #
104     if list[mid_index] < target:
105         #new_list = list[ mid_index + 1:end ]  # 加start和end后,不用传了,否者列表会越界!
106         return find_num(list, target, start=mid_index + 1, end=end)
107     elif list[mid_index] > target:
108         #new_list = list[start:mid_index]
109         return find_num(list, target, start=start, end=mid_index)
110     else:
111         print('Congratunation! You get it!')
112         return mid_index
113 
114 res = find_num(num_li, 66)
115 print(res)
116 
117 # 结果是出现了,还发现一个问题,如果查找的数不在列表中,会报错:RecursionError: maximum recursion depth exceeded in comparison
118 
119 # 4、第四次
120 def find_num(list, target, start = 0, end = None):
121     end = len(list) if end is None else end
122     if end > start:
123         mid_index = (end - start)//2 + start   #
124         if list[mid_index] < target:
125             return find_num(list, target, start=mid_index + 1, end=end)
126         elif list[mid_index] > target:
127             return find_num(list, target, start=start, end=mid_index)
128         else:
129             print('Congratunation! You get it!')
130             return mid_index
131     else:
132         return '该数列中没有你找的数!'
133 
134 res = find_num(num_li, 44)
135 print(res)
View Code

 

你可能感兴趣的:(Python进阶-Ⅸ 递归 二分法)