一、问题引入
1. 斐波那契数列(Fibonacci Sequence)
F(0) = 1, F(1) = 1.......F(n) = F(n-1) + F(n-2)
最简单的编程实现算法为:
def fib(n):
if n == 0:
return 0
if n == 1:
return 1
return fib(n-1) + fib(n-2)
我们可以测试下当 n = 10, n = 20 , n =40 时,程序运行时间成指数级增长,说明此算法的时间复杂度为指数级。
下面我们分析下上面递归函数的执行过程,如下图所示:
不难发现,在递归计算中存在重复计算,如浅蓝色圆圈圈出的部分,就重复计算了三次。在此基础上我们可以对上一版的递归程序进行改进,使得重复计算的部分均只计算一次。
一个简单的方法就是 创建一个memo数组,存放计算的fib(n) 的值,如果 传入的 n已经计算过,便直接返回值,若没被计算,便将计算后得到的值存入到memo数组中。这样保证了每次n只计算一次,不用每次都递归求解 n 对应的fib值。
此方法成为 记忆化搜索:
n = 40
memo = [-1 for i in range(n+1)]
def fib(n):
if n == 0:
return 0
if n == 1:
return 1
if memo[n] != -1:
return memo[n]
memo[n] = fib(n-1) + fib(n-2)
return memo[n]
a = fib(n)
print(a)
二、动态规划
上面的两种方法均基于递归的思想,即自上而下的解决问题。假设子问题已经得到解决,并设置终止条件,从而得到最终问题的解。
而动态规划问题的思想是自下而上的的解决问题,由最初的子问题一步步得到最终的结果。
动态规划算法:
def fib(n):
memo = [0 for i in range(n+1)]
memo[0] = 0
memo[1] = 1
for i in range(2, n+1):
memo[i] = memo[i-1] + memo[i-2]
return memo[n]
a = fib(55)
print(a)
有了记忆化搜索为什么还需要动态规划???动态规划可以最少的循环次数(递归方法对应的就是递归次数)来实现问题的求解,我们可以写程序比较两者的执行时间和程序循环次数。至于为什么动态规划会比递归少,暂时还没想明白。
因此,动态规划整体上是优于记忆化搜索的。
三、动态规划介绍
维基百科定义:将原问题拆解成若干子问题,同时保存子问题的答案,使得每个子问题只求解一次,最终获得原问题的答案
我们可以用一张图来表示递归问题,记忆化搜索, 动态规划 三者之间的联系与区别
参考:
慕课网 刘宇波《玩转算法面试》课程