1. 基本解法
几乎是小白闭着眼都能写出的代码:
为了不产生指数级别的时间复杂度,只需要将中间过程中计算的斐波那契数列数值都记录下就可,改进代码如下:
/**
* 斐波那契数列的实现类
* @author Gastby
*
*/
public class FormatTest {
/**
* 哈希表用来存放中间生成的的斐波那契数值
*/
static HashMap<Integer, Long> map = new HashMap<Integer, Long>();
public static void main(String[] args) {
PrintStream pw = System.out;
int n = 50;
pw.println(fb1(n)); //直接打印第50个斐波那契数列
pw.println(fb2(n));
pw.println(fb3(n));
}
/**
* 递归调用,求算中间第n个斐波那契数值
* @param n
* @return
*/
private static Long fb1(int n) {
if(n == 1) {
if(map.containsKey(n)) //先判断是否已经存入了哈希表中
return map.get(n);
else
map.put(n, (long)1);
return map.get(n);
} else if(n == 2) {
if(map.containsKey(n))
return map.get(n);
else
map.put(n, (long)1);
return map.get(n);
} else {
long v1, v2;
if(map.containsKey(n-1))
v1 = map.get(n-1);
else {
v1 = fb(n-1);
map.put(n-1, v1);
}
if(map.containsKey(n-2))
v2 = map.get(n-2);
else {
v2 = fb(n-2);
map.put(n-2, v2);
}
return v1 + v2;
}
}
}
当然这样的做法太复杂了,其实根本用不着哈希表,只需要数组就可以解决问题。
2. 数组自下向上求解第n个数值;
通过将结果备忘在一个n维数组中,然后依次取出前两位求算得出后一位的结果并保存,时间复杂度O(n),空间复杂度是O(n);实现代码如下:
/**
* 数组求解第n个斐波那契额数列数值
* @param n 输入的第n个数
* @return 返回该数值
*/
private static int fb2(int n) {
if(n == 1)
return 1;
else if(n == 2)
return 1;
else {
int[] t = new int[n];
t[0] = 1;
t[1] = 1;
for(int i=2; i<n; i++) {
t[i] = t[i-1] + t[i-2];
// System.out.println(i + "--" + t[i]);
}
return t[n-1];
}
}
- 但其实在只需要求解第n个斐波那契额数值的时候,你中间缺保留计算了n-1个不需要存储数,一定程度上造成了空间的浪费,所以如果真的从节省空间的角度考虑的化,代码可以从以下角度进一步优化,只用两个变量和一个临时变量的空间大小就可计算得出最终第n个所需数值的代码如下,同样自下向上,代码实现如下:
private static int fb3(int n) {
int a1=1, a2=1;
if(n == 1) return a1;
if(n == 1) return a2;
for(int k=2; k<n; k++) {
if(k % 2 == 1) a1 = a1 + a2;
if(k % 2 == 0) a2 = a1 + a2;
}
if(n % 2 == 0) return a1;
return a2;
}
3. 利用矩阵求算的解法;
斐波那契数列递推公式如下:
不妨通过以下矩阵形式观看递推公式:
而我们则只需要求出A矩阵的n-1次幂结果,就可以最后快速得出F(n)的结果了,而对于n次幂的简化计算,则可以通过判断奇偶的形式以对数级别的运算量得出,这里后续补上代码如下;
/*
为了方便计算,形如下式:矩阵保存数值如下aij代表第i行第j列数值;
*/
struct matrix {
int a00, a01, a10, a11;
};
void multiplyMatrix(matrix &m, int n) {
int u, v, s, t;
for(int i=0; i<n; i++) {
u=m.a00+m.a01;
v=m.a00;
s=m.a10+m.a11;
t=m.a10;
m.a00=u;
m.a01=v;
m.a10=s;
m.a11=t;
}
}
int main() {
struct matrix m = {1,1,1,0};
multiplyMatrix(m, 5);
printf("%d ", m.a00);
printf("%d\n", m.a01);
printf("%d ", m.a10);
printf("%d ", m.a11);
return 0;
}
- 实际上,我们还可以以这样的视角进行计算,由于
通过假设下式成立:
而通过数学归纳法可最终得证上式成立(证明过程省略);
然后也同上,直接通过计算A矩阵的n次幂结果最终得出F(n)项的数值;实现在代码上,如在c语言里可以用一个结构体存储矩阵信息,java或c++则可以使用自定义的class来完成矩阵的定义和幂求算,具体代码实现如下:
void multiplyMatrix(matrix &m, int n) {
if(n<2) return;
multiplyMatrix(m, n/2);
int u, v, s, t;
u=m.a00*m.a00+m.a01*m.a10;
v=m.a00*m.a01+m.a01*m.a11;
s=m.a10*m.a00+m.a11*m.a10;
t=m.a10*m.a01+m.a11*m.a11;
m.a00=u;
m.a01=v;
m.a10=s;
m.a11=t;
if(n&1) {
u=m.a00+m.a01;
v=m.a00;
s=m.a10+m.a11;
t=m.a10;
m.a00=u;
m.a01=v;
m.a10=s;
m.a11=t;
}
}
但实际上,我们在上述计算过程中,多算了不少数值,比如我们只需要求算F(n),却把F(n+1)和F(n-1)都算了出来了,这无疑增加了计算量,《算法时空》里给出了另一个非常惊艳的计算方法,一定程度上更加优化了计算量;
目前见过时间复杂度最低的解法
和上述解法中与其计算
不如计算
那么如何计算呢?不妨借用前面已经得证的结论:
则当 n = 2m 时,下式成立:
-
从而当 n=2m时
可通过计算
得出最后结果。 -
当 n=2m+1时
同理可通过计算
得出最后结果。
具体实现代码后续补上如下:
/*
为了方便计算,a1为上面的值,a2为下面的值;
*/
struct matrix {
int a1,a2;
};
void multiplyMatrix(matrix &m, int n) {
if(n<1) return;
int u, v;
multiplyMatrix(m, n/2);
if(n&1) {
u=m.a1*m.a1+2*m.a1*m.a2;
v=m.a1*m.a1+m.a2*m.a2;
} else {
u=m.a1*m.a1+m.a2*m.a2;
v=2*m.a1*m.a2-m.a2*m.a2;
}
m.a1=u;
m.a2=v;
}
int main() {
struct matrix m = {1,0};
multiplyMatrix(m, 4);
printf("%d ", m.a1);
printf("%d\n", m.a2);
return 0;
}
由此可见,常见数学知识的运用对于解决上述问题会多么的方便!
学好数学,很重要!