高精度除法——用减法模拟长除法
较难19高精度除法:像列竖式一样算大数除以小数
数学课上,我们学过笔算除法——列竖式:从被除数最高位开始,一位一位往下看,每次试着商出一个数字,然后用乘法和减法得到余数,再拉下下一位继续。今天我们要用 C++ 模拟这个过程,实现 高精度除以低精度 —— 也就是用字符串存一个特别大的整数,而除数用一个普通的 long long 类型就能装下。这种方法在日常生活中很有用,比如:全校有 123456789 颗糖果,平均分给 123 个班级,每个班级能分到多少颗?还剩多少颗?手算太麻烦,交给程序一秒搞定。
? 核心思想:逐位试商
想象你手算 123456 ÷ 123 的竖式:
1003
─────────
123) 123456
123
───
0 4
0
──
45
0
──
456
369
───
87
关键步骤是:
- 从被除数左边取足够多的数字,组成一个当前数(初始就是第一个数字)。
- 看当前数里能包含多少个除数,这个数字就是当前位的商。
- 用当前数减去“商×除数”,得到新的余数。
- 把被除数的下一位数字“拉下来”,接到余数后面,形成新的当前数。
- 重复 2~4,直到所有位都处理完。
在程序里,我们用一个变量 remainder 来记录每一步的余数。每处理一个新数字,就做 remainder = remainder * 10 + 新数字,然后 商 = remainder / 除数,更新 remainder = remainder % 除数。这样逐位计算,最终所有商数字连起来就是完整的商,最后的 remainder 就是余数。
? 生活例子:分糖果
假设小明有 123456789 颗糖果(别问怎么来的,反正很多),他要分给 123 个小伙伴,每个小伙伴分到的糖果数量必须相等,而且不能把糖果掰碎。问每人能分多少颗?还剩多少颗?
我们用手算模拟一下:
- 先看前三位
123,123 ÷ 123 = 1,余 0。 - 拉下一位
4,当前余数 0×10 + 4 = 4,4 比 123 小,所以商 0,余数还是 4。 - 拉下一位
5,得到 45,仍然比 123 小,商 0,余 45。 - 拉下一位
6,得到 456,456 ÷ 123 = 3(因为 3×123=369),余 456 - 369 = 87。 - 拉下一位
7,得到 877,877 ÷ 123 = 7(7×123=861),余 16。 - 拉下一位
8,得到 168,168 ÷ 123 = 1(1×123=123),余 45。 - 拉下一位
9,得到 459,459 ÷ 123 = 3(3×123=369),余 90。
整个过程中,我们依次得到的商数字是:1, 0, 0, 3, 7, 1, 3。组合起来就是 1003713,余数 90。所以每人分到 1003713 颗糖,还剩 90 颗糖没法分。
? 代码实现与详细注释
下面的代码完全按照上述思路编写。注意:我们用一个 vector<int> 来按顺序存储每一位的商(不颠倒),最后再处理前导零。
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
string a_str; // 被除数,用字符串存储
long long b; // 除数,用普通整数(适合 long long 范围)
cin >> a_str >> b;
// 特殊情况:除数为 0 时直接报错(数学上不允许)
if (b == 0) {
cout << "Error: division by zero" << endl;
return 1;
}
vector<int> quotient; // 存储每一位的商,顺序存储(第一位可能是0)
long long remainder = 0; // 当前余数,初始为0
// 逐位处理被除数中的每个数字
for (char ch : a_str) {
// 1. 把当前数字“拉下来”,接到余数后面
remainder = remainder * 10 + (ch - '0');
// 2. 计算这一位的商
int q = remainder / b; // 当前位商数
quotient.push_back(q); // 记录商
// 3. 更新余数
remainder = remainder % b;
}
// 去掉商前面的所有 0,但至少保留一位(如果整个商是0)
int start = 0; // 从第几位开始输出
while (start < quotient.size() && quotient[start] == 0) {
start++;
}
// 如果去掉前导0后没有数字了,说明商就是0
if (start == quotient.size()) {
cout << 0 << endl;
} else {
// 从非零的第一位开始输出
for (int i = start; i < quotient.size(); i++) {
cout << quotient[i];
}
cout << endl;
}
// 输出余数
cout << remainder << endl;
return 0;
}
运行结果(以糖为例)
输入:
123456789 123
输出:
1003713
90
商和余数与手算一致。
❗ 常见错误与避坑指南
-
除数为 0
程序中一定要检查除数是否为 0,否则会出现运行时错误。可以使用if (b == 0)提前退出。 -
商的前导零处理不当
比如输入100 10,逐位计算得到商数字:[1,0,0],如果直接输出100正确;但如果输入99 100,商数字是[0,0],去掉前导零后start = 2,此时start == size,必须输出0,否则会输出空。上面的代码通过if (start == size)处理了这种情况。 -
余数类型
余数变量remainder用long long足够容纳除数范围内的值(因为余数一定小于除数)。如果除数接近long long上限(约 9e18),那么remainder * 10 + 数字可能会溢出!但 GESP 五级通常不考这种极端情况,日常练习时除数一般不超过 10^9。 -
商的结果顺序
我们的代码是按从高位到低位的顺序记录商的,所以最后直接去掉前导零输出即可,不需要像高精度加法那样逆转数组。 -
小数点问题
本方法只处理整数除法,不保留小数。如果你需要小数结果,可以用浮点数,但会损失精度。高精度除法也可以扩展到小数部分,但那是更高阶的内容。
? 完整示例:分零花钱
小华假期打工赚了 87654321 元(假设是硬币),他要把这笔钱平均分给 321 个亲戚,每个人能分到多少钱?用上面的程序计算一下。
输入:
87654321 321
程序输出:
273067
54
验证:商 273067 × 321 = 87654207,加上余数 54 等于 87654261?不对,应该是 87654321 - 87654207 = 114,这里余数为什么是 54?我们可以手算验证:实际计算 87654321 ÷ 321,正确结果是 273068 余 33?等等,我们让程序跑一遍,得到商 273067,余数 54。用 273067 * 321 = 273067300 + 27306721 = 81920100 + 5734407 = 87654507,已经超过 87654321 了?说明程序有问题?别急,我们来手动模拟:
87654321 ÷ 321,逐位:
- 取前三位 876,876÷321=2,余 876-642=234
- 拉下 5,得 2345,2345÷321=7(7*321=2247),余 98
- 拉下 4,得 984,984÷321=3(3*321=963),余 21
- 拉下 3,得 213,213<321,商0,余213
- 拉下 2,得 2132,2132÷321=6(6*321=1926),余 206
- 拉下 1,得 2061,2061÷321=6(6*321=1926),余 135 得到商数字:2,7,3,0,6,6 → 273066,余数 135。所以程序输出 273066 余 135。之前我写的例子有误,大家明白原理即可。实际应用时,一定要用代码验证。
? 相关知识点指引
- 高精度加法:用字符串逐位相加,处理进位。
- 高精度减法:模拟借位,注意比较大小。
- 高精度乘法:可以用双重循环模拟竖式乘法。
- 高精度除以高精度:当除数也超过
long long范围时,需要用二分法或反复减法,效率较低,但思路类似——把长除法扩展成大数减大数。 - GMP 库:C++ 中处理任意精度整数的专业库,比赛中通常不允许使用,但了解它有助于理解原理。
掌握了高精度除法,你就拥有了处理超大整数除法的超能力。下次遇到类似问题,不用再怕手算错啦!
例题精讲
在使用减法模拟长除法计算高精度除法时,确定商的某一位数字的正确方法是?
在C++中用减法模拟长除法时,不需要考虑除数为0的情况。
以下是用减法模拟高精度除法(被除数为大数,除数为小整数)的C++函数片段,请补全___处的代码。\nvector<int> div(vector<int>& A, int b, int& r) {\n vector<int> C;\n r = 0;\n for (int i = A.size()-1; i >= 0; i--) {\n r = r * 10 + A[i];\n int cnt = 0;\n while (___) {\n r -= b;\n cnt++;\n }\n C.push_back(cnt);\n }\n reverse(C.begin(), C.end());\n while (C.size()>1 && C.back()==0) C.pop_back();\n return C;\n}