优化斐波那契数列递归的计算
这段时间我在系统的学习一下算法4,并计划学完每一章遍写一个总结,期间遇到的一些问题和思路我也想分享给大家。希望前行的路上我们一起加油!
斐波那契数列指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波那契数列以如下被以递推的方法定义:F(1)=1,F(2)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 3,n ∈ N*)
第一个递归是根据函数得到的最直观的一个
private static long F(int N) { times++;//用于计算执行函数次数 if (N == 0) return 0; if (N == 1) return 1; return F(N - 1) + F(N - 2); }
我们声明一个静态量times来计算函数执行的次数,也就是递归调用的次数+1;
我们计算12组值,并列举出来:
N: 0 0 || times: 1
N: 1 1 || times: 1
N: 2 1 || times: 3
N: 3 2 || times: 5
N: 4 3 || times: 9
N: 5 5 || times: 15
N: 6 8 || times: 25
N: 7 13 || times: 41
N: 8 21 || times: 67
N: 9 34 || times: 109
N: 10 55 || times: 177
N: 11 89 || times: 287
N: 12 144 || times: 465
通过观察我们会发现运行次数为f(n+2) = f(n+1) + f(n) + 1;和斐波那契数的增长是一样的
我们可以通过数学的方式计算出斐波那契数列的通项公式:
算出其时间复杂度约为O(2^n),指数增长级别。
现在列出两个方法可将其复杂度降至O(n):
1,添加一个数组来存储先前的数据,当你计算出f(0),f(1)的值,便将其存入一个数组之中(牺牲空间换取时间),比如你要算f(2),那么就在数组里面拿到f(1)和f(0)的值,同时在数组里存入f(2):
private static long getF(int N) { long[] f = new long[N + 1];//用一个数组去进行一个缓存 return F(N, f); } private static long F(int N, long[] f) { times++; if (f[N] == 0) { if (N == 1) f[N] = 1; else if (N > 1) f[N] = F(N - 1, f) + F(N - 2, f); } return f[N]; }
2,运用迭代的思想,每次计算缓存当前两个数的值,可以较如上方法可以降低空间复杂度:
public static long FBetter(int N) { long ahead1 = 0; long ahead2 = 1; long ahead3 = ahead1 + ahead2; for (int i = 2; i <= N; i++) { ahead1 = ahead2; ahead2 = ahead3; ahead3 = ahead1 + ahead2; } return ahead3; }