2775 字
14 分钟
C++高精度算法完全入门教程
C++高精度算法完全入门教程
📚 什么是高精度算法?
当我们需要计算超出 int
、long long
范围的大数时,就需要用到高精度算法。
为什么需要高精度?
// int 最大值约为 2×10^9 (21亿)// long long 最大值约为 9×10^18
// 但如果要计算:// 123456789012345678901234567890 + 987654321098765432109876543210// 这些数已经超出了 long long 的范围!
核心思想
把大数用字符串或数组存储,模拟手算的过程!
就像我们小学学的竖式计算一样:
- 加法:从右往左,逐位相加,满10进1
- 减法:从右往左,逐位相减,不够就借位
- 乘法:每一位分别相乘,再相加
- 除法:模拟长除法的过程
🔧 准备工作
数据存储方式
我们用 vector
#include <iostream>#include <vector>#include <string>#include <algorithm>using namespace std;
// 将字符串转换为数组(倒序存储,方便进位)vector<int> stringToVector(string s) { vector<int> num; // 从后往前遍历,存入数组 for (int i = s.length() - 1; i >= 0; i--) { num.push_back(s[i] - '0'); // 字符转数字 } return num;}
// 将数组转换为字符串(用于输出)string vectorToString(vector<int> num) { string s = ""; // 去除前导零 bool started = false; for (int i = num.size() - 1; i >= 0; i--) { if (num[i] != 0) started = true; if (started) s += (num[i] + '0'); } if (s == "") return "0"; // 特殊情况:结果为0 return s;}
💡 为什么倒序存储? 因为加减法从个位开始,倒序存储后,索引0就是个位,方便处理进位!
➕ 高精度加法
算法思路
模拟手工竖式加法:
- 从最低位(个位)开始相加
- 如果和 ≥ 10,向高位进1
- 处理完所有位后,检查最高位是否还有进位
代码实现
// 高精度加法:A + Bvector<int> add(vector<int> A, vector<int> B) { vector<int> C; // 存储结果 int carry = 0; // 进位
// 处理所有位(取较长的长度) for (int i = 0; i < A.size() || i < B.size(); i++) { // 获取当前位的数字(如果越界则为0) int x = (i < A.size()) ? A[i] : 0; int y = (i < B.size()) ? B[i] : 0;
// 当前位的和 = A的当前位 + B的当前位 + 进位 int sum = x + y + carry;
// 存储当前位结果(取个位) C.push_back(sum % 10);
// 计算进位(取十位) carry = sum / 10; }
// 处理最后的进位 if (carry > 0) { C.push_back(carry); }
return C;}
使用示例
int main() { string a, b; cout << "请输入两个大数:" << endl; cin >> a >> b;
vector<int> A = stringToVector(a); vector<int> B = stringToVector(b); vector<int> C = add(A, B);
cout << "结果:" << vectorToString(C) << endl;
return 0;}
// 输入:// 123456789012345678901234567890// 987654321098765432109876543210// 输出:// 1111111110111111111011111111100
➖ 高精度减法
算法思路
模拟手工竖式减法:
- 确保被减数 ≥ 减数(否则结果为负)
- 从最低位开始相减
- 如果当前位不够减,从高位借1(当前位+10)
- 去除结果前导零
比较大小函数
// 比较两个大数:A >= B 返回truebool compare(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; // 相等返回true}
减法实现
// 高精度减法:A - B (要求 A >= B)vector<int> subtract(vector<int> A, vector<int> B) { vector<int> C; int borrow = 0; // 借位
for (int i = 0; i < A.size(); i++) { // 获取当前位的数字 int x = A[i]; int y = (i < B.size()) ? B[i] : 0;
// 当前位的差 = A的当前位 - B的当前位 - 借位 int diff = x - y - borrow;
// 如果不够减,借位 if (diff < 0) { diff += 10; // 从高位借10 borrow = 1; // 标记借位 } else { borrow = 0; // 不需要借位 }
C.push_back(diff); }
// 去除前导零 while (C.size() > 1 && C.back() == 0) { C.pop_back(); }
return C;}
使用示例
int main() { string a, b; cin >> a >> b;
vector<int> A = stringToVector(a); vector<int> B = stringToVector(b);
// 确保 A >= B if (!compare(A, B)) { swap(A, B); cout << "-"; // 结果为负数 }
vector<int> C = subtract(A, B); cout << vectorToString(C) << endl;
return 0;}
// 输入:1000000000000 999999999999// 输出:1
✖️ 高精度乘法
1. 高精乘单精(大数 × 小数)
适用场景:A
是大数,b
是普通整数(不超过 10^9)
算法思路
1 2 3 4 × 5 --------- 6 1 7 0
从低位到高位,每位都乘以 b
,处理进位。
代码实现
// 高精度乘单精度:A × bvector<int> multiplyInt(vector<int> A, int b) { vector<int> C; int carry = 0; // 进位
// 遍历A的每一位 for (int i = 0; i < A.size(); i++) { // 当前位乘以b,再加上进位 int product = A[i] * b + carry;
// 存储当前位结果 C.push_back(product % 10);
// 计算进位 carry = product / 10; }
// 处理剩余进位 while (carry > 0) { C.push_back(carry % 10); carry /= 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 = stringToVector(a); vector<int> C = multiplyInt(A, b);
cout << vectorToString(C) << endl;
return 0;}
// 输入:123456789 999// 输出:123333333111
2. 高精乘高精(大数 × 大数)
适用场景:两个数都是大数
算法思路
模拟竖式乘法:
1 2 3 × 4 5 6 --------- 7 3 8 (123 × 6) 6 1 5 0 (123 × 5, 左移1位) 4 9 2 0 0 (123 × 4, 左移2位) --------- 5 6 0 8 8
代码实现
// 高精度乘高精度:A × Bvector<int> multiply(vector<int> A, vector<int> B) { // 结果最多有 A.size() + B.size() 位 vector<int> C(A.size() + B.size(), 0);
// A的每一位 × B的每一位 for (int i = 0; i < A.size(); i++) { for (int j = 0; j < B.size(); j++) { // A[i] × B[j] 放在 C[i+j] 位置 C[i + j] += A[i] * B[j]; } }
// 处理进位 for (int i = 0; i < C.size() - 1; i++) { if (C[i] >= 10) { C[i + 1] += C[i] / 10; // 进位 C[i] %= 10; // 保留个位 } }
// 去除前导零 while (C.size() > 1 && C.back() == 0) { C.pop_back(); }
return C;}
使用示例
int main() { string a, b; cin >> a >> b;
vector<int> A = stringToVector(a); vector<int> B = stringToVector(b); vector<int> C = multiply(A, B);
cout << vectorToString(C) << endl;
return 0;}
// 输入:123456789 987654321// 输出:121932631112635269
➗ 高精度除法(高精÷单精)
算法思路
模拟长除法:
商 -------除数 | 被除数 ------- 余数
从高位到低位处理,每次用当前余数×10 + 当前位,除以除数。
代码实现
// 高精度除单精度:A ÷ b,返回商,r为余数vector<int> divide(vector<int> A, int b, int &r) { vector<int> C; r = 0; // 余数初始化为0
// 从最高位开始除(注意:A是倒序存储的) 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 = stringToVector(a); int remainder; vector<int> C = divide(A, b, remainder);
cout << "商:" << vectorToString(C) << endl; cout << "余数:" << remainder << endl;
return 0;}
// 输入:123456789 999// 输出:// 商:123580// 余数:369
🎯 完整代码示例
将所有功能整合到一起:
#include <iostream>#include <vector>#include <string>#include <algorithm>using namespace std;
vector<int> stringToVector(string s) { vector<int> num; for (int i = s.length() - 1; i >= 0; i--) { num.push_back(s[i] - '0'); } return num;}
string vectorToString(vector<int> num) { string s = ""; bool started = false; for (int i = num.size() - 1; i >= 0; i--) { if (num[i] != 0) started = true; if (started) s += (num[i] + '0'); } return (s == "") ? "0" : s;}
bool compare(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> add(vector<int> A, vector<int> B) { vector<int> C; int carry = 0; for (int i = 0; i < A.size() || i < B.size(); i++) { int x = (i < A.size()) ? A[i] : 0; int y = (i < B.size()) ? B[i] : 0; int sum = x + y + carry; C.push_back(sum % 10); carry = sum / 10; } if (carry > 0) C.push_back(carry); return C;}
vector<int> subtract(vector<int> A, vector<int> B) { vector<int> C; int borrow = 0; for (int i = 0; i < A.size(); i++) { int x = A[i]; int y = (i < B.size()) ? B[i] : 0; int diff = x - y - borrow; if (diff < 0) { diff += 10; borrow = 1; } else { borrow = 0; } C.push_back(diff); } while (C.size() > 1 && C.back() == 0) C.pop_back(); return C;}
vector<int> multiplyInt(vector<int> A, int b) { vector<int> C; int carry = 0; for (int i = 0; i < A.size(); i++) { int product = A[i] * b + carry; C.push_back(product % 10); carry = product / 10; } while (carry > 0) { C.push_back(carry % 10); carry /= 10; } while (C.size() > 1 && C.back() == 0) C.pop_back(); return C;}
vector<int> multiply(vector<int> A, vector<int> B) { vector<int> C(A.size() + B.size(), 0); for (int i = 0; i < A.size(); i++) { for (int j = 0; j < B.size(); j++) { C[i + j] += A[i] * B[j]; } } for (int i = 0; i < C.size() - 1; i++) { if (C[i] >= 10) { C[i + 1] += C[i] / 10; C[i] %= 10; } } while (C.size() > 1 && C.back() == 0) C.pop_back(); return C;}
vector<int> divide(vector<int> A, int b, int &r) { vector<int> C; r = 0; 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() { cout << "=== C++高精度计算器 ===" << endl; cout << "1.加法 2.减法 3.乘法(×小数) 4.乘法(×大数) 5.除法" << endl;
int choice; cin >> choice;
string a, b; vector<int> A, B, C;
if (choice == 1) { cin >> a >> b; A = stringToVector(a); B = stringToVector(b); C = add(A, B); cout << vectorToString(C) << endl; } else if (choice == 2) { cin >> a >> b; A = stringToVector(a); B = stringToVector(b); if (!compare(A, B)) { swap(A, B); cout << "-"; } C = subtract(A, B); cout << vectorToString(C) << endl; } else if (choice == 3) { int num; cin >> a >> num; A = stringToVector(a); C = multiplyInt(A, num); cout << vectorToString(C) << endl; } else if (choice == 4) { cin >> a >> b; A = stringToVector(a); B = stringToVector(b); C = multiply(A, B); cout << vectorToString(C) << endl; } else if (choice == 5) { int num, remainder; cin >> a >> num; A = stringToVector(a); C = divide(A, num, remainder); cout << "商:" << vectorToString(C) << endl; cout << "余数:" << remainder << endl; }
return 0;}
📝 练习题目推荐
入门级
- A+B Problem (大数版) - Luogu P1601
- A-B Problem (大数版) - Luogu P2142
- 高精度乘法 - Luogu P1303
进阶级
- 阶乘计算 - 计算 n! (n ≤ 1000)
- 斐波那契数列 - 计算第 n 项 (n ≤ 10000)
- 大数幂运算 - 计算 a^b
💡 常见问题
Q1: 为什么要倒序存储?
A: 因为进位是从低位向高位进行的,倒序存储后,索引小的就是低位,方便从前往后遍历处理进位。
Q2: 如何判断结果为0?
A: 如果去除前导零后,向量为空或只剩一个0,则结果为0。
Q3: 时间复杂度是多少?
A:
- 加减法:O(max(len(A), len(B)))
- 乘法(高精×单精):O(len(A))
- 乘法(高精×高精):O(len(A) × len(B))
- 除法:O(len(A))
Q4: 可以用string代替vector吗?
A: 可以,但vector操作更方便,尤其是处理进位时。
🎓 总结
高精度算法的核心:
- 用数组/向量存储大数
- 模拟手工计算过程
- 注意处理进位/借位
- 去除前导零
记住:从简单到复杂,先理解原理,再写代码!
加油,算法新手们!💪
本文完整代码已在 GCC 11.4 测试通过
有问题欢迎在评论区讨论!
C++高精度算法完全入门教程
https://wuxie233.com/posts/cpp-high-precision/