大数运算的算法

在数学运算的过程中 ,经常碰到这样的问题:参与运算的 数字很大或者对运算结果的精度要求很高。无论何种计算机 语言 ,在描述数据类型时都有一定的精度和位数要求 ,比如说 16 位整型数 (int) 所能表示的范围为 - 32768~32767 ,实数 (float)所能表示的精度为小数点后 8 位等 ,每种类型的数据都 有其精度和位数限制。超过 20 位有效数字的数值一般就无 法表示了。所以 ,在上述情况下 ,采用一般的程序设计无法满 足要求 ,必须采用高精度的数学运算才能实现。如下例: 示例 1 :

Fibonacci Numbers
A Fibonacci sequence is calculated by adding the previous
two members of the sequence , with the first two members being
both 1. f (1) = 1 , f(2) = 1 , f (n > 2) = f (n - 1) + f(n - 2)

Your task is to take a number as input , and print that Fi2b onacci number.

 Sample Input

100

 Sample Output

 354224848179261915075

Note: No generated Fibonacci number in excess of 1000 digits will be in the test data , i. e. f (20) = 6765 has 4 digits.

这个示例的计算结果可能很大 ,以至于所有的数据类型 都无法表达。所以我们必须用高精度数学运算相应的算法进 行求解。

1.大数存储的实现

作为实现大数存储最常见的一类方法是利用数组。将一 个有 n 位的大数存入数组 ,每个数组的一个元素表示一位十 进制数 ,若是 n 没有超过普通 PC 机允许的定义范围 ,这种算 法是简单易行的。 如果出现超大数 ,则可以采用万进制的方法加以存储 ,在 此就不多做介绍了。

 2.大数计算的算法

 这里仅对大数加法的算法做详细描述 ,剩下的运算给出 算法 ,具体程序实现过程留给读者思考。

2. 1 大数加法

 看下面一个例子:

 122345678902345

+    34567890012

 122380246792357

在上面的加法运算中 ,并没有考虑参与运算的数的位数 , 即使有小数也是一样。每次运算时只是利用加法运算的规则 对参与运算的每一位进行运算 ,每次运算都是在 10 以内进 行 ,并加上了前面的进位。通过这种运算就可以非常准确的 得到运算结果 ,并且可以不考虑位数和精度的问题。 考虑到计算机的存储问题 ,如果我们采用数组来存储参 与运算的每个加数 ,则需要将上面参与运算的数看成字符 ,并 将原来的数翻转 ,即采用下面的运算方法:

   543209876543221

+ 21009876543

753297642083221

我们看到运算结果与实际结果相反 ,因此只需要将结果 再翻转一次输出即可得到正确结果。这样做的好处在于一旦 遇到低位向高位进位时 ,不会出现存储上的问题。因为最高 位存储在数组的最后一个元素 ,其后的存储单元可用于存放 进位 ,同时运算是从数组的第一个元素开始的。 如何用 C语言来求解上面的加法运算呢 ? 我们须首先定 义一个存储结构来存储每一个数。由于我们采用数组来存储 每一个数 ,如果数的最大位数未知 ,则需采用指针方式定义 , 如果已知则可采用数组的方式来定义 ,在该示例中 ,其最大位 数不超过 1000 位 ,因此可采用数组方式来定义 ,但由于每个 数的实际情况各不相同 ,因此还必须定义一个变量来表示每 个数实际的位数。其存储结构用 C语言表示如下:

 typedef struct fanum

{

char fnum[1001] ; / / 数 int len; / / 数的长度

}Fanum;

 基于上面的存储结果及算法 ,我们可采用如下的 C 语言 程序实现两个多位数的加法运算 ,注意该数为整数。

 void strAdd(Fanum3sf1 ,Fanum3sf2)

{
int i ,len ,flag = 0 ;
Fanum sf3 ;
len = sf1 - > len > sf2 - > len ? sf1 - > len:sf2 - > len;
sf3. len = len; for(i = 0 ;i < len;i + + ){
sf3.fnum[i] = sf1 - > fnum[i] - ’0’ + sf2 - > fnum[i] - ’0 ’
+flag;
flag = sf3.fnum[i]/ 10 ;
sf3.fnum[i] = sf3.fnum[i] %10 +’0 ’;
} if (flag = = 1){
sf3.fnum[i] =’1 ’;
sf3. len + + ;
} for(i = 0 ;i < sf3. len;i + + ){
sf1 - >fnum[i] = sf2 - >fnum[i] ;
sf1 - > len = sf2 - > len;
sf2 - >fnum[i] = sf3.fnum[i] ;
sf2 - > len = sf3.len;
}
return;
}

注意此处用 sf1 存储加法运算的结果 ,sf2 用于存储运算 前 sf1 中存储的变量 ,主要是为了能够与上面的示例更相吻 合。 有了上面的函数 ,要完成该题所规定的任务 ,只需要如下 的主程序便可实现。

 int main()
{

FILE*in ,*out ;

Fanum f1 ,f2 ;

long num ,i ;

 in =fopen(“ in. txt” , “r” ) ;

out =fopen(“out. txt” , “w” ) ;

 while( fscanf (in ,“%d” , &num) ! = EOF)

{ f1. len = 1 ; f2. len = 1 ; for(i = 0 ;i < 1001 ;i + + )

{ f1.fnum[i] =’0 ’; f2.fnum[i] =’0 ’; }

f1.fnum[0] =’1 ’;

f2.fnum[0] =’1 ’;

 while(num ! = 2)

{ strAdd( &f1 , &f2) ; num - - ; }

 for(i =f2. len - 1 ;i > = 0 ;i - - )

fprintf (out ,“%d” ,f2.fnum[i] - ’0 ’) ;

fprintf (out ,“ \ n” ) ; }

 fclose(in) ; fclose(out) ;

return 1 ;

}

该题输入数据存放在 in. txt 文件中 ,而输出结果存放在 out. txt 文件中。 大数加法运算实现算法如下:

 (1)将 A、 B 按位对齐;

 (2)低位开始逐位相加;

(3)对结果做进位调整。

2. 2 大数减法

 大数减法运算实现算法如下:

(1)将 A、 B 按位对齐;

(2)低位开始逐位相减;

(3)对结果做借位调整。

2. 3 大数乘法

 大数乘法运算实现算法如下:

 (1)引入 sum2 、 sum1 中间过渡量;

(2)在 n 的每一位上处理 m;

(3)通过每一层循环 ,实现乘法的加法化;

(4)对结果做进位调整。

 2. 4 大数除法

大数除法运算实现算法如下:

 (1)引入 al 来标识 a 的长度 , bl 来标识 b 的长度;

 (2)测算 a 和 b 的长度;

(3)高位开始 ,对位做减法 ,并完成借位;

 (4)高位开始逐位计算商; (5)整理商 , 产生余数。

 2. 5 大数取模

在取模运算中用到了上面的除法运算 ,只需返回余数即 可。

 3.结论

大数运算实际上都是建立在位运算的基础上的 ,实际上 就是对存储大数的数组里每一个数组项进行操作。将大数翻 转过来存储是为了防止在计算过程中出现进位不好表示和溢 出的问题。这种处理也可以看作是对字符串的处理 ,即把这 个数组里的每一位数字转化成字符进行处理。同时如果涉及 到小数 ,如果是加法运算 ,可以把小数点前后的数据分开处 理;如果是乘法运算 ,可将其转换成整数运算之后 ,再转换成 小数。
参考文献: [1]王永祥.超高精度超大数算法与程序设计[M].陕西:西安交通 大学出版社. 1990. 6. [2]王昌锐.大数论[M]. 台北:徐氏基金会. 1970. [3]王金荣 等.大数模乘算法的分析与研究[J ]. 计算机工程与应 用. 2004. 24. [4]Robert L. Kruse ,Alexander J. Ryba.“Data Structures And Pro2g ram Design In C+ + ” . Higher Education Press Pearson Education. 5 , 2001.

}

你可能感兴趣的:(ACM算法_大数运算)