递归与动态规划的不同
递归与动态规划是两个十分基本的算法,它们使用的思路都是分而治之(将一个大问题拆解成一个小问题),我在刚开始学算法时,也是不明白这两者有什么不同,在经过这么久的学习后,我也是对此有了自己的理解(如有不对,欢迎指正)。
下面便是我觉得,递归和动态规划的2个不同:
1.递归是从上而下(从大问题到小问题),而动态规划是由下而上(先解决小问题最后到大问题);
2.动态规划会储存每个小问题的结果,从而它的计算速度会比递归要快。(代价是动态规划的空间复杂度更高,即用空间换取的时间)。
现在大家可能还不是很理解上面两点,让我举个例子来说明一下:
斐波那契数列。
在数学上,斐波那契数列以如下被以递推的方法定义:F(1)=1,F(2)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 3,n ∈ N*),即:新的一项等于前两项之和
即指的是这样一个数列:1、1、2、3、5、8、13、21、34…
下面让我们来求第n项的斐波那契数列的值:
这是一个经典的递归和动态规划问题,下面让我来用Python代码来演示这两种算法,希望大家通过代码可以理解我说的这两者的不同
递归:
def recur_F(n):
if n == 1 or n == 2: # 递归结束条件
return 1
return recur_F(n-1) + recur_F(n-2) # 递归
可以看出,递归是将大n由上而下,逐渐拆解成2的n次方个小问题,这样的缺点就是,有些值会重复计算,例如,输入n=5,那它会将n=3计算两遍(第一次5变成4和3的时候一次,第二次4拆解成3和2的时候一次)。如下图所示,2和1标红代表递归结束,
这样当n比较大的时候,就会多算很多次没有必要的运算,自然时间就慢了
动态规划:
def dp_F(n):
res = [0] * n # 用来记录 n = [1,n] 的全部值
res[0], res[1] = 1, 1 # 第一位和第二位都为1
for i in range(2, n):
res[i] = res[i-1] + res[i-2] # 从 n=3 开始向后更新
return res[n-1]
可以看出,动态规划与递归的最大不同就是它创建了一个数组(空间),来储存后面可能要用到的所有值,这样,就可以避免重复运算的过程,因为后面要用到的值都可以直接来数组里取。还有一点不同就是,它是从前往后进行运算的。
当然,当n比较大时,动态规划需要的空间就很多了,我们可以进行一下优化:
def dp_F_s(n):
if n == 1 or n == 2:
return 1
fn_1 = 1
fn_2 = 1 # 只需要存储 每次运算需要的两位数值就行
cur = 0
for i in range(3, n+1):
cur = fn_1 + fn_2 # 后一项 = 前两项之和
fn_1 = fn_2
fn_2 = cur # 更新这两位数值
return cur
这样,就可以减少动态规划的空间复杂度。当然,这里的思路还是从前往后运算,只不过它所需要的空间变成了2。(之前是n)
**
总结一下,这两者的两个不同:
1.递归是由上而下,动态规划是从下而上;
2.动态规划的运算速度更快。
**
以上便是个人理解的递归与动态规划的不同,如有错误,欢迎指正!