写在前面的话:递归程序是一把利刃,特别是在python程序中可以得以很好的发挥,如果接触少了或者练习少了,感觉它是困难的,甚至只知道这个名词却不敢放心使用。下面的所有问题都可以用递归程序来写,递归不只是单纯的把程序写出来,而且还要验证、优化程序,通过此次练习希望可以达到熟练运用的程度——2021.2.18
目录
- 1-兔子的斐波拉契数列
- 2-河内塔问题,移动大小饼
- 3-字符串匹配问题
- 4-组合问题,不讲究次序
- 5-排列问题,讲究次序
- 6-24点求解问题
- 7-人字形铁路问题
- 8-8皇后问题
1-兔子的斐波拉契数列
1,1,2,3,5
某一个数字是前两个数字的和。
#原始的递归程序
def get_rabbits(months):
if(months <=2):
return 1
return get_rabbits(months-1)+ get_rabbits(months-2)
#优化后的递归运行程序,有很多计算过的
def get_rabbits2(months,store:dict = None):
if store is None:
store = {}
if(months <=2):
return 1
if months in store:
return store[months]
result = get_rabbits2(months-1,store)+ get_rabbits2(months-2,store)
store[months]=result
return result
# print(get_rabbits(7))
for months in range(1,500):
print(months,get_rabbits2(months))#优化后的版本,速度很快
# print(months,get_rabbits(months))
2-河内塔问题,移动大小饼
def hanoi(src,mid,dst,num):
if num == 1:
pass
# print("%s, %d ==> %s"%(src,1,dst))
else:
hanoi(src,dst,mid,num-1)
# print("%s, %d ==> %s" % (src, num, dst))
pass
hanoi(mid, src, dst, num - 1)
def hanoi2(src,mid,dst,num,stor:dict=None):
if stor is None:
stor = {}
if num == 1:
return
# print("%s, %d ==> %s"%(src,1,dst))
key = (src,dst,mid,num)#使用元组作为key
if key in stor:
return stor[key]
hanoi2(src,dst,mid,num-1,stor)
# print("%s, %d ==> %s" % (src, num, dst))
pass
hanoi2(mid, src, dst, num - 1,stor)
stor[key]=None
for num in range(1,400+1):
print('-'*50)
print(num)
# hanoi("A","B","C",num)#A是最早存放杆子,B是中转,C是目的,n盘子数量
hanoi2("A","B","C",num)#A是最早存放杆子,B是中转,C是目的,n盘子数量,优化后的
3-字符串匹配问题
*(星号)可以代表一个字符串,?(问号)代表一个字符,写一个程序进行判定搜索字符串的时候是否匹配,匹配返回True,不匹配返回False
def match2(s,p,stor = None):#s:源字符串,p模式字符串
if len(p)==0:
return len(s)==0
if stor is None:
stor = {}
key = (s,p)
if key in stor:
return stor[key]
ch = p[0]
result = True#随便设定一个初始值
if ch == "?":
result = len(s)>0 and match2(s[1:],p[1:],stor)
elif ch == "*":
result = match2( s,p[1:],stor) or len(s)>0 and match2(s[1:],p,stor)#and 比or优先级高
else:
result = len(s)>0 and s[0]== ch and match2(s[1:],p[1:],stor)
stor[key]=result
return result
print(match2('abaab','a*b'))#成立
print(match2('ab','a*b'))#成立
print(match2('abaaba','a*b'))#不成立
print(match2('abaab','a?aab'))#成立
print(match2('abaab','a?ab'))#不成立
# print(match('abaab','a*b'))#成立
# print(match('ab','a*b'))#成立
# print(match('abaaba','a*b'))#不成立
# print(match('abaab','a?aab'))#成立
# print(match('abaab','a?ab'))#不成立
4-组合问题,不讲究次序
概率中组合计算符号C
# 组合问题
# C(5,2) = 10
# C(6,3) = 20
# C(4,2) = 6
# C(n,m) = ? 0<=m <= n
# 从n中抽取m个小球,不讲次序
def comb(n,m):
#1-递归边界
if m == 0 or m ==n:
return 1
#2-递归推导
return comb(n-1,m-1)+comb(n-1,m)
#优化后的代码
def comb2(n,m,stor = None):
#1-递归边界
if m == 0 or m ==n:
return 1
if stor is None:
stor = {}
key = (n,m)
if key in stor:
return stor[key]
#2-递归推导
result = comb2(n-1,m-1,stor)+comb2(n-1,m,stor)
stor[key] = result
return result
for n in range(1,50+1):
for m in range(n+1):
print("C(%d,%d) = %d"%(n,m,comb2(n,m)))
# print("C(%d,%d) = %d"%(n,m,comb(n,m)))
5-排列问题,讲究次序
概率中组合的计算符号P
#排列问题,讲究次序
def perm(n,m):
if m ==0:
return 1
if m ==1:
return n
#include the first ball
result = m*perm(n-1,m-1)
if n>m:
result += perm(n - 1, m) # not include the first ball
return result
for n in range(1,10+1):
for m in range(n+1):
print("P(%d,%d) = %d"%(n,m,perm(n,m)))
6-24点求解问题
# 2 5 7 11 == 24
#在中间添加符号,点数范围:1~13
#参数设计
import numpy as np
import itertools as it
def make(lists,target):#主程序
print(numbers)
resultList = get_exps(numbers)
for value,exp in resultList:#把表达式中所有操作符组合计算一遍
if value == target:
print(exp)
def get_exps(numbers):#核心程序,numbers = [2,5,7,11]
#return:[(25,'2+5+7+11'),(3,'2+5+7-11'),...]
print("numbers:",numbers)
if len(numbers) == 1:
return [(numbers[0],str(numbers[0]))]
result = []
total = {e for e in range(len(numbers))}#{0,1,2,3}
for left in range(1,len(numbers)): #left = 1,2,...,n-1
for left_ids in it.combinations(total,left):#left_ids = {0},{1},{2},{3},{0,1},{0,2},{0,3},{1,2},{1,3}....
right_ids = total - set(left_ids) #right_ids = {1,2,3},{0,2,3,},{0,1,3},{0,1,2},{2,3}...
print('LeftID:', set(left_ids))
print('RightID:', right_ids)
left_numbers = [numbers[i] for i in left_ids] #[2],[5],[7],[11]...
right_numbers = [numbers[i] for i in right_ids] #[5,7,11],[2,7,11],[2,5,7]
for left_value,left_exp in get_exps(left_numbers):
for right_value,right_exp in get_exps(right_numbers):
value = left_value + right_value
exp = '(%s + %s)'%(left_exp,right_exp)
result.append((value,exp))
value = left_value - right_value
exp = '(%s - %s)' % (left_exp, right_exp)
result.append((value, exp))
value = left_value * right_value
exp = '(%s * %s)' % (left_exp, right_exp)
result.append((value, exp))
value = left_value / right_value
exp = '(%s / %s)' % (left_exp, right_exp)
result.append((value, exp))
return result
def get_numbers(num):
return list(np.random.randint(1,13+1,[num]))
# numbers = get_numbers(4)
numbers = [11, 6, 3, 10]
make(numbers,24)
7-人字形铁路问题
题目:如下图,当有n个小车过人字形铁路有几种可能
#人字形火车问题
#思路,关注第一辆车,它在第一个位置有几种可能,在第二个位置有几种情况,第三个位置有几种情况。。。
# def get_train(n):
# if n<=1:
# return 1
# result = 0
# for i in range(n):
# result += get_train(i)* get_train(n-1-i)
# return result
def get_train2(n,store = None):
if n<=1:
return 1
if store is None:
store = {}
key = n
if key in store:
return store[key]
result = 0
for i in range(n):
result += get_train2(i,store)* get_train2(n-1-i,store)
store[key] = result
return result
for n in range(70+1):
print('get_train(%d) = %d'%(n,get_train2(n)))
# print('get_train(%d) = %d'%(n,get_train(n)))
8-8皇后问题
题目:
皇后是国际象棋中的一个棋子,它可以上、下、左、右、斜着走,且格数不限,请设计个程序判定,如果棋盘为8x8的时候能否将8个皇后放进棋盘,使其不处于危险中(不被其他皇后吃掉)
推广:
程序应该也可以测试2个皇后在22的棋盘能否放置、3个皇后在33的棋盘中能否放置…8个皇后在8*8的棋盘能否放置
分析:
该程序如果用递归来做则需要设定好递归程序参数和递归边界
定义参数:
参数1:总皇后个数,也代表棋盘的网格数,一行或者一列只能放置一个皇后
参数2:在放置第几个皇后
参数3:皇后在棋盘中的位置,可以将位置装在列表中,后续可以根据位置进行打印输出
递归边界:从第一个皇后开始放置,如果放置成功则迭代放置第二个皇后,以此类推,当所有皇后都可以放置下去,则记录好放置的位置,返回放置成功;其中只要有一个皇后放置不下去,则返回皇后放置失败
#递归的主函数,程序功能返回布尔类型,表示可不可以放置
def Queens(num:int,idx:int = 0,dist:list = None ):#num :代表总共几个皇后,和网格;dist:皇后所在的位置,放置在列表中;idx正在放置的皇后编号
if idx == num:
return True
if dist is None:
dist = []
for col in range(num):
if putOk(idx,col,dist):
dist.append(col)
#如果可以放置则进一步判断下一个皇后是否可以放置,开始递归调用
if(Queens(num,idx+1,dist)):
return True
del dist[-1]#因为上一步递归调用了,最后的一个需要删除,递归调用结束
return False
#程序功能:判定这个位置可不可以放置皇后,返回布尔类型
def putOk(position_row,position_col,dist):#position_row:放置的是第几个皇后,因为一行或者一列只能放置1个皇后,所以它可以代表y方向,position_col:因为是从x方向一行行遍历的,所以它代表皇后放置x方向位置,dist上面所有皇后放置的位置
for row ,col in enumerate(dist):
if position_col == col or abs(position_row-row) == abs(position_col - col):#第一个条件判定是否在一行中,第二个条件判定是否在一条斜线上
return False
return True
if __name__ == "__main__":
num = int(input("Please input queens number(int type):"))
print(Queens(num))#输出是否可以放置成功
#打印放置的位置
positionList = []
if Queens(num,dist=positionList):
for col in positionList:
print("- "*col,end="")#继续不换行输出
print("Q",end="")
print("- "*(num - col -1))
else:
print("No solution!")