递归与动态规划(Recursion & Dynamic programming)
递归(Recursion)
普通程序员使用迭代,天才程序员使用递归!
所谓递归,就是有去有回。在数学与计算机科学中,是指在函数的定义中使用函数自身的方法。
递归的基本思想,是把规模较大的一个问题,分解成规模较小的多个子问题去解决,而每一个子问题又可以继续拆分成多个更小的子问题。
递归对问题的处理顺序,是遵循了先入后出(也就是先开始的问题最后结束)的规律。
先入后出?栈!
广义递归问题的处理,需要用栈来解决。所以时间长且非常消耗空间,但是代码简洁
递归需要满足的两个条件:
- 调用函数自身
- 设置了自身正确的返回值
拓展阅读:维基百科 - 递归
求阶乘
- 迭代写法
def factorial(n):
result = n
for i in range(1, n):
result *= i
return result
number = 5
result = factorial(number)
print("%d 的阶乘是:%d" % (number, result)) #5 的阶乘是:120
- 递归写法
def factorial(n):
if n ==1: #1.有结束条件
return 1
else:
return n * factorial(n-1) #2.有调用自身
number = 5
result = factorial(number)
print("%d 的阶乘是:%d" % (number, result)) #5 的阶乘是:120
斐波那契数列(Fibonacci)
斐波那契数列也叫黄金分割数列。
在数学上,费波那契数列是以递归的方法来定义:
- F(0) = 0
- F(1) = 1
- F(n) = F(n - 1) + F(n - 2)(n >= 2)根 = 左子树 + 右子树
用文字来说,就是费波那契数列由0和1开始,之后的费波那契系数就是由之前的两数相加而得出。首几个费波那契系数是:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233……(OEIS中的数列A000045)
拓展阅读:维基百科 - 斐波那契数列
用递归实现斐波那契数列
def fab(n):
if n < 1:
print("输入有误!")
return -1
if n == 1 or n == 2:
return 1
else:
return fab(n - 1) + fab(n - 2)
n = 20
list = []
for i in range(1,n+1):
list.append(fab(i))
print(list)
#结果[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
递归就是分治思想,就像剥圆白菜,直到剥没了,动作停止
汉诺塔(Tower of Hanoi)
我的电子词典上就有这个游戏,上课无聊的时候玩的那叫一个过瘾,可以说我对这个游戏是相当的不陌生了
有三根杆子A,B,C。A杆上有 N 个 (N>1) 穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至 C 杆:
- 每次只能移动一个圆盘;
- 大盘不能叠在小盘上面。
拓展阅读:维基百科 - 汉诺塔
def hanoi(n, a, b, c):#a是起始棍,b是中间棍,c是终点
if n == 1:
print(a, "--->", c) #只有一个圆盘那就直接拿过去就行了
else:
hanoi(n - 1, a, c, b) #将前n-1个盘子从a移动到b上
print(a, "--->", c) #将最底下的最后一个盘子从a移动到c上
hanoi(n - 1, b, a, c) #将b上的n-1个盘子移动到c上
n = 3 #圆盘数
hanoi(n, 'A', 'B', 'C')
#结果
#A ---> C
#A ---> B
#C ---> B
#A ---> C
#B ---> A
#B ---> C
#A ---> C
任务完成!
python - 递归学习视频:
动态规划(Dynamic programming))
图解
摘录于《算法图解》
以上的都建议自己手推一下,然后知道怎么回事,核心的部分是142页核心公式,待会代码会重现这个过程,推荐没有算法基础的小伙伴看这本书《算法图解》很有意思的书,讲的很清晰,入门足够
小结
动态规划: 动态规划表面上很难,其实存在很简单的套路:当求解的问题满足以下两个条件时, 就应该使用动态规划:
- 主问题的答案 包含了 可分解的子问题答案 (也就是说,问题可以被递归的思想求解)
- 递归求解时, 很多子问题的答案会被多次重复利用
动态规划的本质思想就是递归, 但如果直接应用递归方法, 子问题的答案会被重复计算产生浪费, 同时递归更加耗费栈内存, 所以通常用一个二维矩阵(表格)来表示不同子问题的答案, 以实现更加高效的求解。
Python实现
# 动态规划
import numpy as np
# 定义重量
weight = {}
weight["water"] = 3
weight["book"] = 1
weight["food"] = 2
weight["jacket"] = 2
weight["camera"] = 1
# 定义价值
worth = {}
worth["water"] = 10
worth["book"] = 3
worth["food"] = 9
worth["jacket"] = 5
worth["camera"] = 6
# 存放行标对应的物品名:
table_name = {}
table_name[0] = "water"
table_name[1] = "book"
table_name[2] = "food"
table_name[3] = "jacket"
table_name[4] = "camera"
# 创建矩阵,用来保存价值表
table = np.zeros((len(weight), 6))
# 创建矩阵,用来保存每个单元格中的价值是如何得到的(物品名)
table_class = np.zeros((len(weight), 6), dtype=np.dtype((np.str_, 500)))
for i in range(0, len(weight)):
for j in range(0, 6):
# 获取重量
this_weight = weight[table_name[i]]
# 获得价值
this_worth = worth[table_name[i]]
# 获取上一个单元格 (i-1,j)的值
if(i > 0):
before_worth = table[i - 1, j]
# 获取(i-1,j-重量)
temp = 0
if(this_weight <= j):
temp = table[i - 1, j - this_weight]
#(i-1,j-this_weight)+求当前商品价值
# 判断this_worth能不能用,即重量有没有超标,如果重量超标了是不能加的
synthesize_worth = 0
if(this_weight - 1 <= j):
synthesize_worth = this_worth + temp
# 与上一个单元格比较,哪个大写入哪个
if(synthesize_worth > before_worth):
table[i, j] = synthesize_worth
if(temp == 0):
# 他自己就超过了
table_class[i][j] = table_name[i]
else:
# 他自己和(i-1,j-this_weight)
table_class[i][j] = table_name[i] + "," + \
table_class[i - 1][j - this_weight]
else:
table[i, j] = before_worth
table_class[i][j] = table_class[i - 1][j]
else:
# 没有(i-1,j)那更没有(i-1,j-重量),就等于当前商品价值,或者重量不够,是0
if(this_weight - 1 <= j):
table[i, j] = this_worth
table_class[i][j] = table_name[i]
print(table)
print("--------------------------------------")
print(table_class)
[[ 0. 0. 10. 10. 10. 10.]
[ 3. 3. 10. 13. 13. 13.]
[ 3. 9. 12. 13. 19. 22.]
[ 3. 9. 12. 14. 19. 22.]
[ 6. 9. 15. 18. 20. 25.]]
--------------------------------------
[['' '' 'water' 'water' 'water' 'water']
['book' 'book' 'water' 'book,water' 'book,water' 'book,water']
['book' 'food' 'food,book' 'book,water' 'food,water' 'food,book,water']
['book' 'food' 'food,book' 'jacket,food' 'food,water' 'food,book,water']
['camera' 'food' 'camera,food' 'camera,food,book' 'camera,jacket,food'
'camera,food,water']]#所以最后我们拿'camera,food,water'这三样
大神文章:
超级推荐的拓展阅读:动态规划(DP)的整理-Python描述
有时间一定要看一下
第五天就这样结束了
当当当当,致谢
- 理解递归的本质:递归与栈 https://blog.csdn.net/bobbypollo/article/details/79891556
- 023递归:这帮小兔崽子 | 小甲鱼主讲 | 鱼C工作室 https://www.youtube.com/watch?v=UTCPuv_tUuI&t=789s
- 动态规划python实现 https://blog.csdn.net/wcandy001/article/details/79714010
- 动态规划(DP)的整理-Python描述https://blog.csdn.net/MrLevo520/article/details/75676160
- 维基百科 - 汉诺塔 https://zh.wikipedia.org/wiki/汉诺塔
- 维基百科 - 斐波那契数列 https://zh.wikipedia.org/wiki/斐波那契数列
- 维基百科 - 递归 https://zh.wikipedia.org/wiki/递归
下次见