大菲波那契数的计算程序
对于较大的n,由于f[n]已超过计算机字长所能表示的范围,如何利用计算机来精确地生成的f[n]值呢?例如,当n=100时,f[1000]的值大大超过了无符号长整数所能表示的范围,很多对程序设计已有相当多经验的程序员对此不知所措。
事实上这一问题的解决非常简单,设想让我们用纸和笔来计算f[100],所要做的无非是反复进行多位数的加法,如果我们将每位数用数组的一个单元来表示,一个菲波那契数用一个数组表示,只要设计一个多位数的加法程序,相应地,将前面计算小菲波那契数的程序加以改造,问题不就解决了吗?!
为此,定义一个多位数的结构:
#define RADIX
10
typedef struct tagMPInt
{
int length;
int bit[MAXBITS];
}
MPInt;
实现多位数加法的函数为:
void MPIntAdd(MPInt *z, MPInt *x, MPInt *y)
{
int i, carry;
long t;
i = 0;
carry = 0;
while (i < x->length && i < y->length)
{
t = (long)x->bit[i] + y->bit[i] + carry;
z->bit[i] = (int)(t % RADIX);
carry = (int)(t / RADIX);
i++;
}
if (x->length > y->length)
while (i < x->length)
{
t = (long)x->bit[i] + carry;
z->bit[i] = (int)(t % RADIX);
carry = (int)(t / RADIX);
i++;
}
if (x->length < y->length)
while (i < y->length)
{
t = (long)y->bit[i] + carry;
z->bit[i] = (int)(t % RADIX);
carry = (int)(t / RADIX);
i++;
}
if (carry != 0)
{
z->bit[i] = carry;
i++;
}
z->length = i;
}
大菲波那契数的计算程序为:
// File: FibBig.C
#include "MPArth.h"
void FibTable()
{
MPInt
F0, F1;
MPInt
f0, f1, f;
int i, n;
FILE *fp = fopen("fib.dat", "w");
n = 250;
MPIntSetValue1(&F0, 1);
// Set fib[0]
fprintf(fp, "fib[ 0]=1/n");
MPIntSetValue1(&F1, 1);
// Set fib[1]
fprintf(fp, "fib[ 1]=1/n");
f0 = &F0;
f1 = &F1;
for (i = 2; i <= n; i++)
{
MPIntAdd(f0, f0, f1);
// Swap f0, f1
f = f0;
f0 = f1;
f1 = f;
fprintf(fp, "fib[%3d]=", i);
MPIntPrint(f1);
}
fclose(fp);
}
程序MPAdd有很大的改进余地,该程序的设计完全模拟手工演算过程,用一个字长存放一个十进制位,没有充分利用计算机的空间资源和计算能力。容易看出,若将基数RADIX加大,可以加快问题求解的速度。例如,将RADIX的宏定义改为
#define RADIX
10000
不必修改程序,计算速度将加快4倍。当然,RADIX=256时,可以充分地发挥计算机的时空效能,这时已不需要1000位来存放一个菲波那契数,但必须将相应的结果转换为十进制,以符合自然数的书写习惯。