引入
今天在东华oj刷题的时候遇到了大数阶乘,需要用到大数乘法。说来大数乘法以前上算法课的时候学过,不过现在已经忘了……
重新学了下,虽然原理看懂了,但是因为这里面细节太多,防不胜防(加上我比较菜),程序写出来之后调试了很久才得出正确答案。
原理
大数乘法的原理说白了就是利用了手动乘法的计算过程。我们手工做乘法的时候(假设A * B),从A的最后一位开始依次乘以B的每一位取余写下,有进位的时候记下进位。A的每一位乘完都要错开一位写,然后把这些结果错位加起来即可。
代码
/*
大数乘法
*/
#include<stdio.h>
#include<string.h>
#define MAX_SIZE 10
int main() {
char a[MAX_SIZE] = "9999";
char b[MAX_SIZE] = "99999";
int aLen = strlen(a);// a的长度
int bLen = strlen(b);// b的长度
int sumLen = aLen + bLen;// a与b相乘的最大长度
int maxLen = 0, minLen = 0;// a与b中长度较大者和较小者
char maxNum[MAX_SIZE] = "";// a与b中的较大者
char minNum[MAX_SIZE] = "";// a与b中的较小者
int mulCarry = 0, addCarry = 0;// 乘法时的进位,加法时的进位
int temp = 0;// 用于暂存做加法时的中间结果
int sumArr[MAX_SIZE] = {0};// 结果数组
int tempArr[MAX_SIZE] = {0};// a与b相乘时的用于层加的中间结果数组
int i = 0, j = 0;// 用于计数
int k = 0;// k代表错位相加时的起始序号
if (aLen > bLen) {
strcpy(maxNum, a);
strcpy(minNum, b);
maxLen = aLen;
minLen = bLen;
}
else {
strcpy(maxNum, b);
strcpy(minNum, a);
maxLen = bLen;
minLen = aLen;
}
for (i = minLen - 1; i >= 0; i--) {// 开始相乘
k = --sumLen;// 这个地方很妙,k每次都要减1,所以tempArr这个数组在k之后
// 就只能全为零了,结合已经有结果的sumArr数组就可以实现错位
// 相加,我写的时候看了很久才看懂
for (j = maxLen - 1; j >= 0; j--) {// 模拟a与b的各个位相乘
tempArr[k] = ((maxNum[j] - '0') * (minNum[i] - '0') + mulCarry) % 10;
mulCarry = ((maxNum[j] - '0') * (minNum[i] - '0') + mulCarry) / 10;// 更新进位
k--;
}
if (mulCarry > 0) {// 检查进位
tempArr[k] = mulCarry;
}
mulCarry = 0;// 重置进位
for (j = maxLen + minLen - 1; j >= 0; j--) {// 模拟错位相加
temp = (sumArr[j] + tempArr[j] + addCarry);
sumArr[j] = temp % 10;
addCarry = temp / 10;// 更新进位
}
addCarry = 0;// 重置进位
for (j = 0; j < MAX_SIZE; j++) {// 重置错位相加中间结果数组
tempArr[j] = 0;
}
}
i = 0;
while (sumArr[i++] == 0) {// 跳过结果中前面的0
;
}
i--;
for (; i < maxLen + minLen; i++) {// 输出相乘结果
printf("%d", sumArr[i]);
}
printf("\n");
return 0;
}