大数运算(1)—— 加法篇

 前言

  最近遇到一道求阶乘的题目,原以为极其简单,但是阶乘的结果超过了范围最大的基本数据类型的范围,于是就着手研究大数运算(large number computing),本篇先介绍大数加法。

原理

  大数运算的原理其实就是模拟人工计算(注记:再考虑是否有其他算法。注记日期:2017.3.19),人工加法计算步骤如下:

    1.将两个操作数(operand)位数对齐。

    2.从最低位开始,计算两个操作数每位的总和再加上进位数(第一位数为0)的结果,保留其个位数。

    3.若上述的结果大于10,进位数置为1,否则为0。(每位数相加的结果不会超过 9 + 9 = 18 , 因此进位数只有1和0)

    4.重复上述步骤2、3,直到计算完两个操作数其中那个位数小的最高位。

  知晓了步骤,就需要考虑设计储存操作数的数据结构。首先使用列表是毋庸置疑的,而顺序结构和链表结构其实都行,这里为里减少代码量和说明问题,暂且使用顺序结构

  综上,给出操作数的结构体如下:

1 typedef struct operand
2 {
3     int data[100];
4     int digit;    // 记录该数位数
5 } OPERAND;

  注意,这里给出最大位数为100,下面给出的代码中没有考虑溢出问题,请读者自行解决。

实现

  首先,思考上述步骤1的对齐问题,可以定义一个偏移量(offset)来存储两个操作数的位数之差,之后遍历的时候可以给位数少的增加偏移量来达到对齐目的。可是这种做法会增加一定的代码量,因此不妨试着把数字倒着存放,即数组第一个元素存储操作数的最低位(注意前述做法的前提与此相反),这样做就省去了写对齐的代码。

  下面给出该出加法函数,其中结果保存到第一个操作数中

 1 void plus(OPERAND* operand1,OPERAND* operand2)
 2 {
 3     int carry = 0;    // 进位标记
 4     int i = 0 , j = 0;
 5     for(;i < operand1->digit || j < operand2->digit;i++ , j++)
 6     {
 7         int sum = operand1->data[i] + operand2->data[j] 
 8                   + carry;    // 计算该位数总和加上进位数
 9         operand1->data[i] = sum % 10;    // 更新该位数的数据
10         carry = (sum >= 10)? 1 : 0 ;    // 判断是否进位
11     }
12     operand1->digit = (operand1->digit > operand2->digit)?
13                        operand1->digit : operand2->digit ;
14                        // 更新位数
15     if(carry)    // 若最后需要进位
16     {
17         operand1->data[operand1->digit] = 1;
18         operand1->digit += 1;
19     }
20 }

  其中,sum % 10 用于求个位数,还有注意最后别忘了最高位的进位问题。

posted @ 2017-03-19 17:15  ZachLim  阅读(3423)  评论(0编辑  收藏  举报