一. 题目背景
在日常计算中,加减乘除是最常见不过的,放到计算机中,在unsigned long long类型范围内,可以通过高级程序设计语言编译,传给计算机硬件,进行计算,但操作数要是很大的话,大于unsigned long long的范围,就是高精度数与高精度数的操作了。
二. 高精加和高精乘
初阶高精算法原理相当于小学学习的加减乘除,竖式加法乘法我们再熟悉不过了,例如123+789这个算式,用竖式写出来:
1 | 2 | 3 | |
+ | 7 | 8 | 9 |
和 | 8 | 10 | 12 |
进位 | 1 | 1 | |
= | 9 | 1 | 2 |
答案为912,我们一步一步拆分看,先算了每一位的和,留下 mod 10 的结果,下一位加一( / 10)的结果,这也就是高精加算法的结晶了。
同理,高精乘我们小时候也用的是竖式算法,例如1234 * 56
1 | 2 | 3 | 4 | ||
* | 5 | 6 | |||
6* | 6 | 12 | 18 | 24 | |
5* | 5 | 10 | 15 | 20 | |
和 | 5 | 16 | 27 | 38 | 24 |
第一步是将较小数放在下面,然后需要注意计算较小数第二位乘法时,起始位就是本位置,
下一步就是加起来每一位的和,进行与加法一样的套路,留下 mod 10 的结果,下一位加 / 10 .
t | 5 | 16 | 27 | 38 | 24 |
t[-1]/10 | 1 | 3 | 4 | 2 | |
和 | 6 | 19 | 31 | 40 | |
t%10 | 6 | 9 | 1 | 0 | 4 |
ans | 6 | 9 | 1 | 0 | 4 |
答案为69104.
接下来就是对每一步的代码实现了.
三. 代码实现
初始录入数据
正常写数字是从高位开始,但我们的计算从个位开始,所以输入输出都是逆置的
首先输入接收的是两个字符串,定义变量
#define MAXN 100000010
char a[MAXN], b[MAXN];
int A[MAXN], B[MAXN];
int C[MAXN];
主函数中进行输入和逆置将 a[ ] 内字符转为 每一位数交给 A[ ].
scanf("%s", a);
scanf("%s", b);
int sa = strlen(a);
int sb = strlen(b);
int maxsize = Max(sa, sb);
for (int i = sa - 1, j = 0; i >= 0; j++, i--) A[j] = a[i] - '0';
for (int i = sb - 1, j = 0; i >= 0; j++, i--) B[j] = b[i] - '0';
接下来就是算法的核心了
高精加:
加法函数代码:
void add(int A[],int sa, int B[],int sb,int C[]) {
int sizec = Max(sa, sb);
int t = 0;
for (int i = 0; i < sizec; i++) {
t += A[i] + B[i];
C[i] = t % 10;
t /= 10;
}
if (t) C[sizec] = t;
}
int Max(int a, int b) {
if (a > b) return a;
else return b;
}
注意当我们加至最后一位时,并没有把最后可能进位 t 放入答案数组,所以加上特判:
if (t) C[sizec] = t;
高精乘:
乘法函数代码:
void ride(int A[], int sa, int B[], int sb, int C[]) {
int t = 0;
if (sb > sa) return ride(B, sb, A, sa, C);
for (int i = 0; i < sb; i++) {
for (int j = 0; j < sa; j++) {
C[i + j] += B[i] * A[j];
}
}
for (int i = 0; i < sa + sb - 1; i++) {
t += C[i];
C[i] = t % 10;
t = t / 10;
}
if (t) C[sa + sb - 1] = t;
}
因为必须让较小数作为竖式乘法的第二操作数,通过位数长度的比较返回正确的格式:
if (sb > sa) return ride(B, sb, A, sa, C);
乘法中最容易犯错的就是答案数组下标的遍历,较小数下标为 i ,计算每一位乘积时累加到 i + j 下标的答案数组中.
下一步的进位与加法相同,用 t 作为中间量,mod 10 和 / 10.
完整代码
高精加:
#include<stdio.h>
#include<string.h>
void add(int A[], int sa, int B[], int sb, int C[]);
int Max(int a, int b);
#define MAXN 100000010
char a[MAXN], b[MAXN];
int A[MAXN], B[MAXN];
int C[MAXN];
int main(void) {
scanf("%s", a);
scanf("%s", b);
int sa = strlen(a);
int sb = strlen(b);
int maxsize = Max(sa, sb);
for (int i = sa - 1, j = 0; i >= 0; j++, i--) A[j] = a[i] - '0';
for (int i = sb - 1, j = 0; i >= 0; j++, i--) B[j] = b[i] - '0';
add(A, sa, B, sb, C);
if (C[maxsize]) printf("%d", C[maxsize]);
for (int i = maxsize - 1; i >= 0; i--) printf("%d", C[i]);
return 0;
}
void add(int A[],int sa, int B[],int sb,int C[]) {
int sizec = Max(sa, sb);
int t = 0;
for (int i = 0; i < sizec; i++) {
t += A[i] + B[i];
C[i] = t % 10;
t /= 10;
}
if (t) C[sizec] = t;
}
int Max(int a, int b) {
if (a > b) return a;
else return b;
}
高精乘:
#include<stdio.h>
#include<string.h>
#define MAXN 1000010
void ride(int A[], int sa, int B[], int sb, int C[]);
int A[MAXN], B[MAXN], C[2 * MAXN];
char a[MAXN], b[MAXN];
int main(void) {
scanf("%s", a);
scanf("%s", b);
if (a[0] == '0' || b[0] == '0') {
printf("0");
return 0;
}
int sa = strlen(a);
int sb = strlen(b);
for (int i = sa - 1, j = 0; i >= 0; i--, j++) A[j] = a[i] - '0';
for (int i = sb - 1, j = 0; i >= 0; i--, j++) B[j] = b[i] - '0';
ride(A, sa, B, sb, C);
if (C[sa + sb - 1]) printf("%d", C[sa + sb - 1]);
for (int i = sa + sb - 2; i >= 0; i--) printf("%d", C[i]);
return 0;
}
void ride(int A[], int sa, int B[], int sb, int C[]) {
int t = 0;
if (sb > sa) return ride(B, sb, A, sa, C);
for (int i = 0; i < sb; i++) {
for (int j = 0; j < sa; j++) {
C[i + j] += B[i] * A[j];
}
}
for (int i = 0; i < sa + sb - 1; i++) {
t += C[i];
C[i] = t % 10;
t = t / 10;
}
if (t) C[sa + sb - 1] = t;
}
最后一点注意,乘法若是有0,直接输出0就好啦.
学会了吗,学会了就来练习练习巩固模板吧
加:https://www.luogu.com.cn/problem/P1601
乘:https://www.luogu.com.cn/problem/P1303
本蒟蒻还会继续学习算法与大家分享,一起加油吧