关于斐波那契数列,相信学过函数递归的友友们肯定晓得这是啥,不过为加强动态规划的思想学习,本居开了新的专题——动态规划,在这里将会简要介绍一下动态规划的思想,用该思想来开启一种高效的斐波那契数列的求解之法,并以该例子为开头,深入探究学习动态规划。
啥是动态规划?
用人话来解释就是大事化小,小事化了
的分治
思想
好的,这话好像在哪见过,没错,在学习函数递归的时候,用的就是该思想,但是有所不同的是,动态规划在分治的过程中,对小问题处理好的结果进行了保存,等到之后在处理更大规模问题时可以直接使用这些结果
,而不是每次求大问题时都从小问题入手,吭哧吭哧求大问题。
具体思考问题的四个角度:
- 状态定义
- 状态间的转移方程定义
- 状态的初始化
- 返回结果
就拿本案例来说!
在数学上,斐波那契数列以如下被以递推的方法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)来求第N项斐波那契数列
递归思想
如果用递归的思想来求,非常简单,应该是这样的:
public class Solution {
public int Fibonacci(int n) {
if(n <= 0) {
return 0;
}
//边界条件
if(n == 1 || n == 2) {
return 1;
}
//按照定义总结出的递归式
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
}
代码虽简单,但当n越大,包含的运算的量就会以指数形式爆发增长。
由图可见,当n的值较小的时候,计算是非常简单的,但是当n的值变大时,就会出现大量的重复计算,n为5的时候,Fib(1)就已经2次参与运算,Fib(2)就已经3次参与运算,Fib(3)被重复计算了2次,可想而知,当n越变越大时,重复计算的量就会大量增加,计算的速度会随着n的增加肉眼可见的变慢,程序甚至会挂。
因此,若是将每次计算出的小问题存起来就可以很好地提高效率,我们可以准备一个数组来存放小问题的结果!
动归思想
状态定义:Fib(i)的值即斐波那契数列的第i个值
状态间的转移方程定义:Fib(i) = Fib(i - 1) + Fib(i - 2)
状态的初始化:Fib(0) = 0 Fib(1) = 1
返回结果:返回Fib(n)的值即斐波那契数列的第n个值
public class Solution {
public int Fibonacci(int n) {
if(n == 1||n == 2) return 1;
if(n <= 0) return 0;
int[] Fib = new int[n + 1]; //用来存放每次计算出的斐波那契数
Fib[0] = 0;
Fib[1] = 1; //状态的初始化
Fib[2] = 1;
for(int i = 3;i <= n;i ++) {
Fib[i] = Fib[i - 1] + Fib[i - 2]; //转移方程
}
return Fib[n]; //返回所求的第n位斐波那契数
}
}
动归思想(改良)
根据题目所要求的,实际上我们只需要知道第n项的前两项即可,并不需要将斐波那契数列中位数比n小的全部放到数组当中,只需要在循环的过程中不断更新n - 1,n - 2为斐波那契数的值即可。
public class Solution {
public int Fibonacci(int n) {
if(n == 1||n == 2)return 1;
if(n <= 0) return 0;
int ret = 0;
int n1 = 1; //n - 1位的初始值
int n2 = 1; //n - 2位的初始值
for(int i = 3;i <= n;i ++) {
ret = n1 + n2; //转移方程
n2 = n1; //更新n - 2位的值
n1 = ret; //更新n - 1位的值
}
return ret; //返回结果
}
}
完!