写在前面的话:递归程序是一把利刃,特别是在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个小车过人字形铁路有几种可能

用Python递归求兔子数列 python兔子数列用递归解决_递归调用

#人字形火车问题
#思路,关注第一辆车,它在第一个位置有几种可能,在第二个位置有几种情况,第三个位置有几种情况。。。

# 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!")