CC++ & Algorithm

大数除法不用慌:把竖式搬进 C++,几行代码解决“亿”点难题

你有没有想过,如果全校有 123456789 颗糖果,要平均分给 123 个班级,每班能分多少颗?手算要列一长串竖式,用计算器倒是快,但如果是更大的数——比如几百位的大整数,普通计算器根本装不下。这时候,C++ 里用字符串存大数,就能轻松搞定。而背后的原理,其实就是你小学学过的竖式除法。

竖式除法,天然就是“高精度”

回想起手算 123456 ÷ 123 的过程:从被除数最高位开始,看当前几位能包含几个除数,写商,乘出积,减出余数,再拉下一位……每一步只处理一位数字,根本不需要一次性看到整个大数。这个“逐位处理”的思路,正是高精度算法的基础——既然一个数字存不下,那就一位一位地处理。

在程序里,我们用一个变量 remainder 表示“当前余数”。每来一个新数字,就把它接到余数后面,相当于竖式里“拉下一位”:

remainder = remainder * 10 + (ch - '0');
int q = remainder / b;        // 商出这一位
quotient.push_back(q);
remainder = remainder % b;    // 更新余数

就这么几行,配合一个循环,就能完成整个大数除法。商存在一个 vector<int> 里,最后去掉前导零输出,余数就是处理完所有数字后的那个 remainder

思路对了,但别踩这两个坑

这个算法看起来简单,但初学者最容易犯两个错。

第一个坑:除数为 0。 有人觉得,反正我用减法模拟除法,不直接调 / 运算符,是不是就不用管除数为 0 了?大错特错。想一想,如果除数是 0,remainder 永远不会变小,循环永远停不下来,程序直接卡死。数学上除数为 0 无意义,编程里更是灾难。所以进入循环前必须:

if (b == 0) {
    cout << "Error: division by zero" << endl;
    return 1;
}

还有一道判断题也正好考这个:用减法模拟长除法时“不需要考虑除数为 0”是错的——无论是直接用 / 还是用减法模拟,除数为 0 都必须提前处理。这个点看起来简单,但考的就是你对除法本质的理解。

第二个坑:前导零的处理。 比如输入 99 100,逐位计算得到的商是 [0, 0],去掉所有前导零后没有数字了,必须输出 0,而不是什么都不输出。代码如下:

int start = 0;
while (start < quotient.size() && quotient[start] == 0) start++;
if (start == quotient.size()) cout << 0 << endl;
else {
    for (int i = start; i < quotient.size(); i++) cout << quotient[i];
    cout << endl;
}

每一位的商,其实就是“减了几次”

有朋友可能会问:remainder / b 这一步,不是已经用了除法运算符吗?那还算什么“用减法模拟”?

其实,这里用的除法运算符是 C++ 内置的、对 long long 范围内整数的除法。而所谓“高精度除以低精度”,指的是被除数是超大整数,除数还在普通整数范围内。所以我们可以放心用 / 来计算当前位的商。但如果除数也是个超大整数呢?那就真的要用减法模拟了:从被除数当前剩余部分中反复减去除数,每减一次就计数,直到剩余小于除数,计数就是这一位的商。

你可能会遇到这样一道选择题:在使用减法模拟长除法时,确定商的某一位数字的正确方法是什么?答案是“反复减去除数并计数”,因为商位只能是 0~9,循环最多执行 9 次,效率极高。而直接相除、把除数乘以 10 再比较、或者用二分法,都不是正确思路。这道题看似在考“怎么算”,实际是在提醒你:高精度除法的本质就是“反复做减法”,无论被除数多大,这一位上的商都不会超过 9——因为竖式里一位商最大就是 9。

代码片段足够用,剩下的是思路

完整的代码不贴了,网上很多。核心就三步:拉下数字、求商、更新余数。但有一点必须注意:remainder * 10 可能溢出。如果除数接近 long long 的上限(约 9.2e18),那 remainder * 10 + 数字 就爆了。日常题目的除数一般不超过 10^9,不会触发这个问题,但写程序时心里要有这根弦。

再回到开头的分糖果问题。用这个算法,输入:

123456789 123

输出:

1003713
90

每人分到 1003713 颗,还剩 90 颗——和你手算竖式的结果一模一样。你看,高精度除法并不可怕,它就是竖式的机械化。理解了竖式,就理解了它。

进阶方向上,下一步是“高精度除以高精度”。这时除数也成了大数,不能再用 / 直接求商位了。思路依然是竖式,但减去除数的方式从“一步除法”变成“循环减法”,再加上二分查找来加速。再往下,还有压位高精度、Karatsuba 乘法、FFT 优化等等。但无论多么高深的算法,最初的那粒种子,还是你小学列过的那个竖式。


关于作者

我是赵老师,持有 NOI 信息学奥赛教练证书,拥有 15 年以上的软件开发经验,从事信息学少儿编程教学已有 8 年时间。

这些年累计帮助 多名 学生通过编程特长升入自己心仪的目标学校。

如果你在编程学习上有任何疑问,欢迎联系我:18620372957(微信同号)

这篇文章对你有帮助吗?

有用 100%没用 0%

想系统学习这个知识点?查看完整知识点 →