1.引言
最近在做学校网上的OJ题时,碰到了高精度加法和乘法的运算。加法没有太多实现上的困难,但是乘法却卡了一会儿,在网上搜集材料时,很多都是直接贴上了代码,没有具体的分析。为了巩固自己的学习成果并且和大家交流学习,我将学习的过程记录下来,希望可以帮助到有同样困扰的同学。
2.思路
2.1 大整数加法思路的回顾
为了更好的理解高精度乘法处理的基本思路,首先应该回顾一下大整数的加法,再循序渐进到乘法当中。 因为高精度计算中的数据已经远远超过基本数据类型能表示的范围,所以处理这类问题的一个基本思想是将输入的整数转换为数组,然后通过对加法以及乘法的模拟来获取结果。大整数加法的步骤大致有四个:
这里没有太多的注意事项,只要在实现时注意留出'\0'的位置就好
为什么需要对字符数组进行倒序的处理呢?仔细思考一下就可以发现,我们运算时都是从低位运算到高位并且加上相应的进位来进行计算的,如果不对字符数组进行倒序的处理的话,一方面需要从后向前操作数组,很不便利,更大的问题在于在最高位产生进位时,处理多出来的位数很麻烦。如果还有疑问的话,建议大家用纸笔模拟一下数组,就可以得出结果。至于转化就很简单啦,我们需要把字符型转换为对应的整数,才能进行模拟的运算。
倒序与转化的部分可以很简单的实现。
for(i = 0; i < lenA; i++){
arrayA[i] = inputA[lenA - 1 -i] - '0';
}
加法的模拟计算不再赘述,按位相加并且记得处理进位即可。
倒序输出结果是没有什么疑问的,但是在处理前导0时,有一个小坑是如果没有考虑结果为0的情况,会出现WA。这个问题在乘法当中同样存在,要注意。
2.2乘法思路
对于乘法来讲,与加法的思路基本一致,只是在模拟计算上与加法不同。值得一提的是乘法结果位数的估计。使用计算器实验一下就会发现,结果的位数最大为两个乘数的位数之和,所以我们有
int maxLen = lenA + lenB;
真正的重点其实还是落脚在乘法的模拟上,首先我们给出乘法模拟的代码,代码不是很长,但是理解起来可能还是有一些小小的障碍。
for(i = 0; i < lenA; i++){
for(j = 0; j < lenB; j++){
result[i+j] += arrayA[i] * arrayB[j];
result[i+j+i] += (result[i+j]) / 10;
result[i+j] = result[i+j] % 10;
}
}
我们来结合一个乘法竖式来看看乘法的过程
通过对结果的仔细观察,可以发现最终的结果是和乘积的结果+以前乘积的结果+进位 构成的。因为这里确实不好用语言来描述这个过程,大家可以参照着代码和图示,推敲一下,很快就能得到结论。要注意一步到位,直接走到结果,而不要想模拟两个横向加法的过程再相加走弯路。
看到这里,除了一些边边角角的小细节外,相信你已经可以写出解决问题的程序了,我在下面给出了我实现的代码和详细的注释,算法很基础,写的也很简单,有不足之处,希望大家多多指教。
C语言实现
#include
#include//为了动态分配内存
#include//为了使用strlen();
#define MAX_LEN 200
int main(void)
{
char inputA[MAX_LEN + 1];//用于存放输入的数据 +1 给‘\0'留位置
char inputB[MAX_LEN + 1];
int i,j;//循环变量,因为OJ编译器版本问题,没办法在循环中直接定义循环变量
scanf("%s%s",inputA,inputB);
int lenA = strlen(inputA);
int lenB = strlen(inputB);
int maxLen = lenA + lenB;//最大的结果位数
int* arrayA = (int*)malloc(sizeof(int) * lenA);
int* arrayB = (int*)malloc(sizeof(int) * lenB);
int* result = (int*)malloc(sizeof(int) * maxLen);
for(i = 0; i < lenA; i++){//倒序并转换
arrayA[i] = inputA[lenA - 1 -i] - '0';
}
for(i = 0; i < lenB; i++){//倒序并转换
arrayB[i] = inputB[lenB - 1 -i] - '0';
}
for(i = 0; i < lenA; i++){//模拟计算的过程
for(j = 0; j < lenB; j++){
result[i+j] += arrayA[i] * arrayB[j];//刷新每一个结果位上的结果
result[i+j+i] += (result[i+j]) / 10;//求出进位数
result[i+j] = result[i+j] % 10;//进位后原位不需要使用+=
}
}
while(maxLen - 1 > 0 && result[maxLen - 1] == 0){//去除前导0
maxLen--;
}
for(i = maxLen - 1; i >=0; i--){//输出结果
printf("%d",result[i]);
}
return 0;
}