一、前言
1、递归与动态规划
动态规划有时为什么被认为是一种与递归相反
的技术呢?
内容 | 方式 | 语法 | 效率 |
---|---|---|---|
递归 | 自上而下 | 简介 | 不高 |
动态规划 | 自下而上 | 稍微复杂 | 高 |
因为递归是从顶部开始将问题分解,通过解决掉所有分解出小问题的方式,来解决整个问题。
动态规划解决方案从底部开始解决问题,将所有小问题解决掉,然后合并成一个整体解决方案,从而解决掉整个大问题。
使用递归去解决问题虽然简洁,但效率不高。包括 JavaScript 在内的众多语言,不能高效地将递归代码解释为机器代码,尽管写出来的程序简洁,但是执行效率低下。
但这并不是说使用递归是件坏事,本质上说,只是那些指令式编程语言和面向对象的编程语言对递归 的实现不够完善,因为它们没有将递归作为高级编程的特性。
二、 斐波拉契数列
斐波拉契数列定义为以下序列:
0,1,1,2,3,5,8,13,21,34,55,......
可以看到,当 n >= 2,a(n) = a(n - 1) + a(n - 2)。这个数列的历史非常悠久,它是被公元700年一位意大利数学家斐波拉契用来描述在理想状态下兔子的增长情况。
不难看出,这个数列可以用一个简单的递归函数
表示。
function fibo (n) {
if (n <= 0) return 0;
if (n === 1) return 1;
return fibo(n - 1) + fibo(n - 2);
}
这种实现方式非常耗性能
,在n的数量级到达千级别,程序就变得特别慢,甚至失去响应。如果使用动态规划从它能解决的最简单子问题着手的话,效果就很不一样了。这里我们先用一个数组来存取每一次产生子问题的结果,方便后面求解使用。
function fibo (n) {
if (n <= 0) return 0;
if (n <= 1) return 1;
var arr = [0, 1];
for (var i = 2; i <= n; i++) {
arr[i] = arr[i - 1] + arr[i - 2];
}
return arr[n];
}
细心的同学发现,这里的数组可以去掉,换做局部变量来实现可以省下不少内存空间。