算法之递归、分治策略、动态规划以及贪心算法之间的关系

https://blog.csdn.net/tyhj_sf/article/details/53969072算法

转载:http://blog.csdn.net/tyhj_sf/article/details/53969072markdown

引言

最近集中研究计算智能,其中涉及到递归和动态规划,动态规划实现中又用到了递归,突然发现这两个概念的差异分得不太清楚。索性把递归、分治策略、动态规划、贪婪选择之间的联系与区别都一并搞清楚吧。ide

一、分治策略(Divide and Conquer)

将原问题分解为若干个规模较小但相似于原问题的子问题(Divide),递归的求解这些子问题(Conquer),而后再合并这些子问题的解来创建原问题的解。由于在求解大问题时,须要递归的求小问题,所以通常用递归的方法实现,即自顶向下。post

二、动态规划(Dynamic Programming)

动态规划其实和分治策略是相似的,也是将一个原问题分解为若干个规模较小的子问题,递归的求解这些子问题,而后合并子问题的解获得原问题的解。区别在于这些子问题会有重叠,一个子问题在求解后,可能会再次求解,因而咱们想到将这些子问题的解存储起来,当下次再次求解这个子问题时,直接拿过来就是。其实就是说,动态规划所解决的问题是分治策略所解决问题的一个子集,只是这个子集更适合用动态规划来解决从而获得更小的运行时间。即用动态规划能解决的问题分治策略确定能解决,只是运行时间长了。所以,分治策略通常用来解决子问题相互对立的问题,称为标准分治,而动态规划用来解决子问题重叠的问题。性能

动态规划通常由两种方法来实现,一种为自顶向下的备忘录方式,用递归实现,一种为自底向上的方式,用迭代实现。ui

三、贪心算法(Greedy Algorithm)

贪心算法在每一步都作出最优的选择,但愿这样的选择能致使全局最优解。对,只是寄但愿,所以贪心算法并不保证获得最优解,可是它对不少问题确实能够获得最优解,并且运行时间更短。因而可知,贪心算法是带有启发性质的算法。那何时能够用贪心算法呢?当该问题具备贪心选择性质的时候,咱们就能够用贪心算法来解决该问题。 
贪心选择性质:咱们能够经过作出局部最优(贪心)来构造全局最优。只要咱们可以证实该问题具备贪心选择性质,就能够用贪心算法对其求解。好比对于0-1背包问题,咱们用贪心算法可能得不到最优解(固然,也可能会获得最优解),但对于部分背包问题,则能够获得最优解,贪心算法能够做为0-1背包问题的一个近似算法。spa

动态规划与递归的比较

就性能而言,我用递归和动态规划实现了斐波纳契数列计算,递归若是超过40的时候就已经须要很长时间了,40次大概须要1秒左右,可是用动态规划要一亿次,才须要4秒,这个相差的可不是几个数量级的问题。事实上,递归实现的斐波那契数列计算时间复杂度为O(2ⁿ),动态规划实现时间复杂度为O(n)因此,在之后的开发中,尽可能避免使用递归。 
就具体实现上而言,动态规划比普通递归仅仅是多了一步保存子问题计算结果的操做。 
例如,斐波那契数列的递归实现以下:.net

int F(int i) { if(i < 1) return 0; if(i == 1) return 1; return F(i-1) + F(i - 2); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

而用动态规划算法实现是这样:code

int F(int i) { if(knownF[i] != unknown){ return knownF[i]; } if(i == 0) t = 0; if(i == 1) t = 1; if(i > 1) t = F(i - 1) + F(i - 2); return knownF[i] = t; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

四、总结

  1. 分治策略用于解决原问题与子问题结构类似的问题,对于各子问题相互独立的状况,通常用递归实现;
  2. 动态规划用于解决子问题有重复求解的状况,既能够用递归实现,也能够用迭代实现;
  3. 贪心算法用于解决具备贪心选择性质的一类问题,既能够用递归实现,也能够用迭代实现,由于不少递归贪心算法都是尾递归,很容易改为迭代贪心算法;
  4. 递归是实现手段,分治策略是解决问题的思想,动态规划不少时候会使用记录子问题运算结果的递归实现。

参考资料: 
1.http://1661518.blog.51cto.com/1651518/1396943 
2.《算法导论》第三版blog

相关文章
相关标签/搜索