本文主要讲解高精度计算,包括加法、减法、乘法和除法。
对于Python选手,python自带高精度计算;Java也有BigInteger类。但是对于C选手和C++选手,高精度计算的算法还是很重要的。
加法和乘法比较简单,减法和除法要难一些。
java相关的大数类可以参见:Java【大数类】整理
高精度加法
题目描述
给定两个正整数(不含前导 0),计算它们的和。
数据范围
1 ≤ 整数长度 ≤ 100000
解法
cpp
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
vector<int> add(vector<int> &A, vector<int> &B) {
if (A.size() < B.size()) return add(B, A);
vector<int> C;
int t = 0; // t是进位
for (int i = 0; i < A.size(); ++i) {
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_back(a[i] - '0');
for (int i = b.size() - 1; i >= 0; --i) B.push_back(b[i] - '0');
vector<int> C = add(A, B);
for (int i = C.size() - 1; i >= 0; --i) printf("%d", C[i]);
return 0;
}
Java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
char[] a = scanner.next().toCharArray(), b = scanner.next().toCharArray();
int c = 0;
StringBuilder ans = new StringBuilder();
for (int i = a.length - 1, j = b.length - 1; i >= 0 || j >= 0 || c != 0; --i, --j) {
if (i >= 0) c += a[i] - '0';
if (j >= 0) c += b[j] - '0';
ans.append(c % 10);
c /= 10;
}
System.out.println(ans.reverse());
}
}
高精度减法⭐⭐⭐
题目描述
给定两个正整数(不含前导 0),计算它们的差,计算结果可能为负数。
数据范围
1 ≤ 整数长度 ≤ 10^5
解法
CPP
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
bool cmp(vector<int> &a, vector<int> &b) {
/* 判断是否 a 大于等于 b */
int an = a.size(), bn = b.size();
if (an != bn) return an > bn;
for (int i = an - 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) {
int an = a.size(), bn = b.size();
vector<int> c;
int t = 0; // t表示是否借位
for (int i = 0; i < an; i++) {
a[i] -= t; // 减去借位
if (i < bn) a[i] -= b[i];
c.push_back((a[i] + 10) % 10);
if (a[i] < 0) t = 1;
else t = 0;
}
while (c.size() > 1 && c.back() == 0) c.pop_back();
return c;
}
int main()
{
string a, b;
vector<int> A, B, C;
cin >> 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)) C = sub(A, B);
else {
printf("-");
C = sub(B, A);
}
for (int i = C.size() - 1; i >= 0; --i) printf("%d", C[i]);
return 0;
}
Java
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String a = scanner.next(), b = scanner.next();
List<Integer> A = new ArrayList<>(), B = new ArrayList<>(), C = new ArrayList<>();
for (int i = a.length() - 1; i >= 0; --i) A.add(a.charAt(i) - '0');
for (int i = b.length() - 1; i >= 0; --i) B.add(b.charAt(i) - '0');
if (compare(A, B)) C = sub(A, B);
else {
System.out.print("-");
C = sub(B, A);
}
for (int i = C.size() - 1; i >= 0; --i) System.out.print(C.get(i));
}
public static boolean compare(List<Integer> A, List<Integer> B) {
int an = A.size(), bn = B.size();
if (an != bn) return an > bn;
for (int i = an - 1; i >= 0; --i) {
if (A.get(i) != B.get(i)) return A.get(i) > B.get(i);
}
return true;
}
public static List<Integer> sub(List<Integer> A, List<Integer> B) {
int an = A.size(), bn = B.size();
List<Integer> C = new ArrayList<>();
int t = 0;
for (int i = 0; i < an; ++i) {
A.set(i, A.get(i) - t); // 看看这一位有没有被借
if (i < bn) A.set(i, A.get(i) - B.get(i));
C.add((A.get(i) + 10) % 10);
if (A.get(i) < 0) t = 1;
else t = 0;
}
while (C.size() > 1 && C.get(C.size() - 1) == 0) C.remove(C.size() - 1);
return C;
}
}
讲解
注意点包括:
判断a和b的大小;去除结果末尾的所有0;使用(t+10)%10;
高精度乘法
题目描述
给定两个非负整数(不含前导 0) A 和 B,请你计算 A×B 的值。
数据范围
1 ≤ A的长度 ≤ 100000,
0 ≤ B ≤ 10000
解法
和加法类似,每次用大数字的一位乘上整个小数字。
Cpp
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
vector<int> mul(vector<int> A, int b) {
int t = 0; // t类似加法中的进位
vector<int> C;
for (int i = 0; i < A.size() || t; ++i) {
if (i < A.size()) t += A[i] * b;
C.push_back(t % 10);
t /= 10;
}
while (C.size() > 1 && C.back() == 0) C.pop_back(); // 删除前导零
return C;
}
int main()
{
string a;
int b;
cin >> a >> b;
vector<int> A, C;
for (int i = a.size() - 1; i >= 0; --i) A.push_back(a[i] - '0');
C = mul(A, b);
for (int i = C.size() - 1; i >= 0; --i) printf("%d", C[i]);
return 0;
}
Java
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String a = scanner.next();
int b = scanner.nextInt(), c = 0;
List<Integer> A = new ArrayList<>(), C = new ArrayList<>();
for (int i = a.length() - 1; i >= 0; --i) A.add(a.charAt(i) - '0');
for (int i = 0; i < a.length() || c != 0; ++i) {
if (i < a.length()) c += A.get(i) * b;
C.add(c % 10);
c /= 10;
}
for (int i = C.size() - 1; i > 0; --i) {
if (C.get(i) == 0) C.remove(i);
else break;
}
for (int i = C.size() - 1; i >= 0; --i) System.out.print(C.get(i));
}
}
讲解
与加法类似。
高精度除法⭐⭐⭐⭐⭐
题目描述
给定两个非负整数(不含前导 0) A,B,请你计算 A/B 的商和余数。
数据范围
1 ≤ A的长度 ≤ 100000,
1 ≤B ≤ 10000,
B 一定不为 0
解法
Cpp
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
vector<int> div(vector<int> a, int b, int &r) {
vector<int> c;
for (int i = a.size() - 1; i >= 0; --i) {
r = r * 10 + a[i];
c.push_back(r / b);
r %= b;
}
reverse(c.begin(), c.end());
while (c.size() > 1 && c.back() == 0) c.pop_back();
return c;
}
int main()
{
string a;
int b;
cin >> a >> b;
vector<int> A, C;
for (int i = a.size() - 1; i >= 0; --i) A.push_back(a[i] - '0');
int r = 0; // r是余数
C = div(A, b, r);
for (int i = C.size() - 1; i >= 0; --i) printf("%d", C[i]);
printf("\n%d\n", r);
return 0;
}
Java
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String a = scanner.next();
int b = scanner.nextInt();
List<Integer> A = new ArrayList<>(), C;
for (int i = a.length() - 1; i >= 0; --i) A.add(a.charAt(i) - '0');
int r = 0; // r是余数
div(A, b, r);
}
static void div(List<Integer> A, int b, int r) {
List<Integer> C = new ArrayList<>();
// 从高位开始
for (int i = A.size() - 1; i >= 0; --i) {
r = r * 10 + A.get(i);
C.add(r / b);
r %= b;
}
Collections.reverse(C);
while (C.size() > 1 && C.get(C.size() - 1) == 0) C.remove(C.size() - 1);
for (int i = C.size() - 1; i >= 0; --i) System.out.print(C.get(i));
System.out.println("\n" + r);
}
}
讲解
与 加减乘 不同,除法是从高位开始的。
从最高位开始枚举大数字的每一位,在这个过程中注意余数的变化为:
r = r * 10 + a[i];
然后使用余数 r 去除以小数字,加入答案商,然后 r %= b;