简洁高效高精除法
2021-12-05 21:35
266
3
高精除法是高精里面比较麻烦的。并且实现思路很多,这里记录一个模拟竖式计算的思路。
如何模拟十进制的竖式除法?

除法需要一位一位从高到低得出答案,用被除数减去答案和除数的积,得到余数作为下一轮被除数,继续获取下一位答案。
而获取余数的时候,我们将这一位的答案乘上除数,并于正在求解的这一位对齐后相减即可。
如何具体获得每一位的答案呢?一位的范围并不大,直接枚举即可。在压位的情况里(比如每位为1e9),可以在0到1e9之间二分答案。而这样也是符合实际计算的。试想一下,手算100/13
时,心里想的也是“13x7=91小了,13x8过一百了,答案是7”
,其本质就是二分。
另外,我们知道x
位数乘以y
位数只能是x+y
位数或者x+y-1
位,所以x
位数除以y
位数,最多只有x-y+1
位数,从第x-y+1
开始枚举即可。
int lans; struct DecInt { int len, a[MAX]; void trim() { while (len && !a[len-1]) --len; } DecInt operator*(int x) const { //高精乘低精 DecInt ans = DecInt(len); long long num = 0; for (register int i = 0; i < len; i++) { num += (long long)a[i] * x; ans.a[i] = num % MOD; num /= MOD; } if (num) ans.a[ans.len++] = num; return ans.trim(), ans; } DecInt& operator-=(const DecInt &x) { //这里的自减需要将被减数移动lans位后相减 for (int i = 0; i < x.len; i++) { a[i + lans] -= x.a[i]; if (a[i + lans] < 0) a[i + lans] += MOD, a[i + lans + 1]--; } for (int i = x.len + lans; i < len; i++) a[i] < 0 && (a[i] += MOD, a[i+1]--); return trim(), *this; } bool comp(const DecInt &t, const DecInt &divd) { //这里的比较是比较答案和除数的积和被除数,需要将被除数移动ans位 if (t.len != divd.len - lans) return t.len < divd.len - lans; for (int i = t.len-1; ~i; i--) if (t.a[i] != divd.a[i+lans]) return t.a[i] < divd.a[i+lans]; return 1; } DecInt operator/(const DecInt &B) { lans = len - B.len; if (lans < 0) return DecInt("0", 1); DecInt ans = DecInt(lans + 1); while (~lans) {//从第lans位开始计算答案 int l = 0, r = MOD-1; while (l < r) { //二分求解第lans位 int mid = l + r + 1 >> 1; comp(B * mid, *this) ? l = mid : r = mid-1; } *this -= B * l;//将被除数减掉本位答案和除数的积 ans.a[lans--] = l; } return ans.trim(), ans; } };
可以看到,每次只取的被除数去掉后面lans位的数来进行操作(比如上例里,第一次除时只比较"435"
,做减法时也是从"435"
开始操作),提高的效率。
另外就是二分答案不能写错,乘积小了增大l
,乘积大了减小r
,而正好相等时,mid
也有可能是答案,不能被排掉。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步