高精度算法浅析
本文简要地介绍高精度算法。这个算法有点像是用代码来模拟人工算数的过程,以此来实现按数位操作的四则运算。
加法
高精度加法通常是两个高精度的整数相加。由于是两个大数,读入时将以
s
t
r
i
n
g
string
string的形式读入,并从低位到高位读入两个
v
e
c
t
o
r
vector
vector容器当中,然后再进行操作。
每次操作将数A、数B的这一位上的数字和上一位操作结束后的进位相加。他们的和再模10之后即为这一位的最终结果,而他们除10得到的商即为操作下一位时要加上的进位。
模板如下:
#include <iostream>
#include <vetcor>
#include <string>
vector<int> add(vector<int>& A, vetcor<int>& B){
vector<int> C;
int t = 0
for (int i = 0; i < A.size() || i < B.size(); i++){
if (i < A.size()) {t += A[i];}
if (i < B.size()) {t += B[i];}
C.push_back(t % 10);
t /= 10;
}
if (t) {C.push_back(t);}
return C;
}
int main(){
string a, b;
vector<int> A, B;
cin >> a >> b;
for (int i = a.size() - 1; i >= 0; i--){
A.push_bakc(a[i] - '0');
}
for (int i = b.size() - 1; i >= 0; i--){
B.push_back(b[i] - '0');
}
auto C = add(A, B);
for (int i = C.size() - 1; i >= 0; i--){
printf("%d", C[i]);
}
return 0;
}
减法
减法与加法类似。首先回忆我们平时手撕减法的时候是怎样做的:从低位开始,A减去B与上一位借的位数的和,若这个和小于0,便和下一位借一个位数,若大于0就直接成为结果(输入C)。
操作第1位时借位应为0,故借位的初始值为0。此后借位值应在每次循环结束后进行更新,以使我们在下一次操作中的“减去”一步中减去正确的借位值。
模板如下:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
bool cmp(vector<int>& A, vector<int>& B){
if (A.size() != B.size()) return A.size() > B.size();
for (int i = A.size() - 1; i >= 0; i--){
if (A[i] != B[i]) return A[i] > B[i];
}
return true;
}
vector<int> sub(vector<int>& A, vector<int>& B){
// 假设A > B
vector<int> C;
int t = 0;
for (int i = 0; i < A.size(); i++){
t = A[i] - t;
if (i < B.size()) t -= B[i];
C.push_back((t + 10) % 10);
if (t >= 0) t = 0;
else t = 1;
}
while (C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}
int main(){
string a, b;
cin >> a >> b;
vector<int> A, B;
for (int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0');
for (int i = b.size() - 1; i >= 0; i--) B.push_back(b[i] - '0');
if (cmp(A, B)){
auto C = sub(A, B);
for (int i = C.size() - 1; i >= 0; i--) printf("%d",C[i]);
}
else{
auto C = sub(B, A);
printf("-");
for (int i = C.size() - 1; i >= 0; i--) printf("%d", C[i]);
}
return 0;
}
乘法
这里只考虑高精度乘一个低精度数的情况。循环在高精度数位操作结束之前或是进位不为0之前都要继续(因为有时候高精度数操作完之后进位还有一大坨数)。
#include <iostream>
#include <string>
#include <vector>
using namespace std;
vector<int> mul(vector<int>& A, int B){
vector<int> C;
int c = 0;
for (int i = 0; i < A.size() || c; i++){
if (i < A.size()) c += A[i] * B;
C.push_back(c % 10);
c /= 10;
}
while (C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}
int main(){
string a;
int B;
vector<int> A;
cin >> a ;
cin >> B;
for (int i = a.size() - 1; i>= 0; i--) A.push_back(a[i] - '0');
auto C = mul(A, B);
for (int i = C.size() - 1; i >= 0; i--) printf("%d", C[i]);
return 0;
}
除法
这里也只考虑高精度除以低精度的情况。
除法跟其他三种操作不一样。在做除法时,我们需要从最高位开始做起。故而在输入A的时候我们需要从高位到低位依次输入。
注意到这样一来,我们得到的结果C的先导0(若存在)将会出现在C的开头,故而设置一个pointer使其达到第一个不为0的数或最后一个0(在C中全为0时)。
#include <iostream>
#include <string>
#include <vector>
using namespace std;
vector<int> div(vector<int>& A, int B, int& r) {
vector<int> C;
for (int i = 0; i < A.size(); i++) {
r = A[i] + 10 * r;
C.push_back(r / B);
r = r % B;
}
return C;
}
int main() {
string a;
int B;
cin >> a;
cin >> B;
int r = 0;
vector<int> A;
for (int i = 0; i < a.size(); i++) A.push_back(a[i] - '0');
auto C = div(A, B, r);
int pointer = 0;
while (pointer < C.size() - 1 && C[pointer] == 0) {
pointer++;
}
for (int i = pointer; i < C.size(); i++) {
printf("%d", C[i]);
}
cout << endl;
printf("%d", r);
return 0;
}
(也有人将C反转过后去除先导0的,这样最后输出时需要从后往前输出)