讨论一个著名的数列——斐波那契数列(Fibonacci sequence),其第0项为0,第1项为1,从其第2项开始,每一项都为前面两项的和,即:
C语言中,我们很自然能够得出如下的递归代码:
#define var __int64
var Fibonacci2(int n) {
if (n <=1) {
return n;
}
return Fibonacci2(n - 1) + Fibonacci2(n - 2);
}
其中var是由宏定义的,如果为int(4字节)类型,将在第47个数溢出,在其他语言中可能不需要考虑这个问题,这不是我们讨论的重点。该算法效率是指数级的,代码简短带来的问题可能就是性能的低下。读者可以尝试用循环和常数辅助空间实现的算法。下面实现一种
的算法,基于等式:
读者若感兴趣,可以用数学归纳法证明。
右式是一个2阶方阵,如果用普通矩阵乘法计算,效率类型也是,还需讨论如何更高效实现矩阵乘方。
对于指数为非负整数的乘方运算,我们可以将指数视为二进制,例如:
可以通过迭代的方式得到权值,即:
具体实现为:
var pow(int a, int n){
int sum = 1;
int temp = a;
while (n) {
if (n % 2) {
sum *= temp;
}
temp = temp * temp;
n /= 2;
}
return sum;
}
将sum和temp的类型视为二维数组,初始的1就改为单位矩阵,以上的思想可以推广到矩阵乘方。
同时,由于sum和temp是对称阵,可以压缩为一维数组。
同样,矩阵乘法不是我们讨论的重点,以下为具体实现:
#include<stdio.h>
#define var __int64
var Fibonacci(int n) {
var sum[] = { 1, 0, 1 };//a11,a12(a21),a22
var temp[] = { 0, 1, 1 };//b11,b12(b21),b22
var k;//辅助空间
while (n) {
if (n % 2) {
k = sum[0] * temp[0] + sum[1] * temp[1];
sum[1] = sum[0] * temp[1] + sum[1] * temp[2];
sum[0] = k;
sum[2] = k + sum[1];
}
k = temp[0] * temp[0] + temp[1] * temp[1];
temp[1] = temp[0] * temp[1] + temp[1] * temp[2];
temp[0] = k;
temp[2] = k + temp[1];
n /= 2;
}
return sum[1];
}
int main() {
for (int i = 0; i < 100; i++) {
printf("%d:%lld ", i, Fibonacci(i));
}
}
很明显,循环内部的基本操作是常数时间,该算法效率优化到了。
但第93个数已经溢出64位整数所能表示的范围,解决这个问题需要实现大整数类。
读者可用time.h库对多种算法单独进行测试,此算法优化幅度是非常大的。