高精度算法是在计算问题涉及的数据范围超过该程序语言的表示能力时,用数组模拟数学运算的一类算法。本节学习高精度的整数四则运算,其中乘法只要求一个因子是高精度,除法只要求被除数是高精度。以下,用大写字母(如 A A A、 B B B)表示高精度的整数,用小写字母(如 b b b)表示用编程语言自带的整数类型就能表达的整数。

以下默认是十进制数表示下的整数四则运算操作,在其它进制表示下也是类似的。

1 高精度整数A+B

模板题:高精度加法。

要计算两个高精度整数 A A A和 B B B的和,可以用列竖式的思想,从最低位开始,将低位加起来然后进位,每次将进位 t t t都和两者尚未计算的最低位 A [ i ] A[i] A[i]和 B [ i ] B[i] B[i]加起来,其除以 10 10 10的余数就是这一位的值,商就是进位 t t t,所以:

由于 A A A和 B B B的位数不一定是一样的,这里不妨假设 A A A的长度总是 > = B >=B >=B的长度(如果输入时不满足,就交换 A A A和 B B B再做高精度加法),这样计算的时候就只要把 A A A的每一项都加完,相应的位置如果 B [ i ] B[i] B[i]还存在就加,不存在就不用加了。

在加完之后还要判断一下进位 t t t,如果还有进位的话,就要再在结果中加一位。只要用一个if判断一次就可以了,不需要用while来做,因为最后 t t t的值一定是 0 ≤ 9 < 10 0 \leq 9 < 10 0≤9<10的,因为以 A A A的长度,加上一个不超过其长度的 B B B,结果 C C C最大能达到的长度也就是 A A A的长度 + 1 +1 +1,不可能更长了。

由于是从最低位往高位一位一位加,而读取数据的时候是从高位向低位读的,所以这里将 A A A和 B B B都排成从低位向高位的,这样方便计算。相应地,得到的 C C C也是从低位到高位排的,最后记得翻转一下。

#include <iostream>
#include <string>
#include <vector>using namespace std;// 高精度加法,A和B都是从低位到高位存的
vector<int> add(vector<int>& A, vector<int>& B) {// 如果A的长度不足B的长度,计算B+A,总是保证A是较长的一个if (A.size() < B.size()) return add(B, A);// 保存实时的进位值int t = 0;// 保存计算结果vector<int> C;// 从低位到高位遍历A(较长者)的每一位for (int i = 0; i < A.size(); i ++ ) {t += A[i]; // 先把A[i]加进来if (i < B.size()) t += B[i]; // 如果B还没遍历完,把B[i]也加进来C.push_back(t % 10); // 对10取模就是结果C的这一位的值t /= 10; // 除以10得到的就是向高位的进位值}// 最后,如果进位值还不是0,说明还有一个向高位的进位,补充进去if (t) C.push_back(t);return C;
}int main() {// 由于两个数很大,用字符串读入string a, b;cin >> a >> b;// 翻转过来,从低位到高位存到数组里vector<int> 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');// 计算高精度加法,得到的结果C也是从低位到高位存的auto C = add(A, B);// 从高位到低位输出才是正常阅读次序for (int i = C.size() - 1; i >= 0; i -- ) cout << C[i];cout << endl;return 0;
}

2 高精度整数A-B

模板题:高精度减法。

要计算两个高精度整数 A A A和 B B B的差,也是用列竖式的思想。这里需要先判断一下 A A A和 B B B哪个大,如果 B B B比 A A A大,那么 A − B A-B A−B的结果是 − ( B − A ) -(B-A) −(B−A),所以只需要实现一个较大的自然数减去一个较小的自然数这个过程就可以了。

在做减法的时候,已经确定传入的 A A A是 > = B >=B >=B的了,所以 A A A的长度也是 > = B >=B >=B的,可以用和前面计算高精度加法时候类似的思想。从低位到高位遍历 A A A中的每一位,用 t t t表示来自上一位的借位(借位只能是 0 0 0或者 1 1 1) 。

那么这一位的减法实际上就是 A [ i ] − t − B [ i ] A[i] - t - B[i] A[i]−t−B[i](如果 B B B已经遍历完了,就不用减 B [ i ] B[i] B[i])。如果这个值是负的,那么就说明这一位的减法是向更高一位借了 1 1 1的,那么下一轮 t t t就要变成 1 1 1,否则下一轮 t t t的值就是 0 0 0。

向高位借 1 1 1的效果就是在本位加了一个 10 10 10,所以本位减法的计算结果可以直接用 + 10 +10 +10之后再 m o d 10 mod~10 mod 10来计算,这样不论有没有借位计算的都是正确的。

需要注意,最终的减法结果可能存在前导 0 0 0,所以需要删除高位的连续 0 0 0。然而当减法结果是 0 0 0时候,需要保留一个 0 0 0,所以在删除高位 0 0 0的时候注意一下只剩一个数的时候也要停止删除了。

#include <iostream>
#include <string>
#include <vector>using namespace std;// 高精度整数比较,A和B都是从低位到高位存的
// 如果A>=B返回true,否则返回false
bool cmp(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];// 如果每一位都一样,说明A和B相等,也返回truereturn true;
}// 高精度减法,A和B都是从低位到高位存的
vector<int> sub(vector<int>& A, vector<int>& B) {// 保存计算结果vector<int> C;// 从低位到高位遍历A的每一位for (int i = 0, t = 0; i < A.size(); i ++ ) {// 减去来自上一位的借位tint val = A[i] - t;// 如果B还没遍历完,还要减去B的这一位的数if (i < B.size()) val -= B[i];// 这一位的减法结果C.push_back((val + 10) % 10);// 判断一下是否存在向更高一位的借位,如果借了也就是借1if (val < 0) t = 1;else t = 0;}// 去除高位的连续0,注意如果计算结果是0要保留一个0,所以保持C至少有一位数就行while (C.size() > 1 && C.back() == 0) C.pop_back();return C;
}int main() {// 由于两个数很大,用字符串读入string a, b;cin >> a >> b;// 翻转过来,从低位到高位存入数组vector<int> 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');// 计算高精度减法,存到C中vector<int> C;if (cmp(A, B)) C = sub(A, B);else {cout << '-';C = sub(B, A);}// 从高位到低位输出才是正常阅读次序for (int i = C.size() - 1; i >= 0; i -- ) cout << C[i];cout << endl;return 0;
}

3 高精度整数A*b

模板题:高精度乘法。

这里 A A A是一个高精度整数, b b b是一个普通精度的整数,计算它们的乘法也是用列竖式的方法,但是因为 b b b不是一个高精度的数,只需要从 A A A低位到高位,每次把对应位置的数字 A [ i ] A[i] A[i]乘以 b b b再加上来自低位的进位 t t t就可以了,而不需要把 b b b的每一位拆开来。

乘的结果模 10 10 10就是结果 C C C的这一位上的数,而除以 10 10 10的结果就是进位。在 A A A的每一位都乘完之后,进位的值可能还没用完,所以要重复“模 10 10 10放在下一位,除以 10 10 10作为进位”这个操作。

结果 C C C中的高位可能有连续的 0 0 0,所以还是像高精度减法中那样处理一下高位的连续 0 0 0,同时注意如果结果就是 0 0 0要留下一个 0 0 0。

#include <iostream>
#include <string>
#include <vector>using namespace std;// 高精度乘法,计算大整数A和普通量级的整数b的相乘结果
// A从低位到高位存储
vector<int> mul(vector<int>& A, int b) {// 保存计算结果vector<int> C;// 保存来自上一位的进位值int t = 0;// 从低位到高位遍历A计算乘法结果for (int i = 0; i < A.size(); i ++ ) {// 把A的这一位A[i]乘以b的结果加上上一位的进位值tt += A[i] * b;;// 模10的结果就是这一位的值C.push_back(t % 10);// 除以10就是向高位的进位值t /= 10;}// 最后的进位可能很大,要处理剩余的进位while (t) {// 每次模10的结果构成新的一位C.push_back(t % 10);// 然后除以10作为向高位的进位值t /= 10;}// 处理高位的连续0while (C.size() > 1 && C.back() == 0) C.pop_back();return C;
}int main() {// 读取大整数a和普通量级的整数bstring a;int b;cin >> a >> b;// a从低位到高位存到A里vector<int> A;for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');// 高精度乘法计算A*bauto C = mul(A, b);// 从高位到低位输出结果for (int i = C.size() - 1; i >= 0; i -- ) cout << C[i];cout << endl;return 0;
}

4 高精度整数A/b

模板题:高精度除法。

这里 A A A是一个高精度整数, b b b是一个普通精度的整数,计算它们的除法也是用列竖式的方法,但是因为 b b b不是一个高精度的数,只需要从 A A A高位到低位(注意这里和前面都不一样),每次把对应位置的数字 A [ i ] A[i] A[i]加上来自高位的余数 r ∗ 10 r * 10 r∗10再除以 b b b就可以了,而不需要把 b b b的每一位拆开来。

这里高位的余数 r r r是在高位那个量级上的,所以在这一位(较低一位)上要乘以 10 10 10才行。

和前面保持一致, A A A还是从低位到高位来存,但是计算的时候从高位向低位依次做除法,得到的结果 C C C是从高位到低位存的。所以还要记得把 C C C用reverse翻转一下,变成从低位到高位存的,然后还是要删除一下高位的连续 0 0 0。

在代码实现中,函数div的返回值返回的是 A A A除以 b b b的商,而余数 r r r从参数表里用引用的形式返回。

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>using namespace std;// 高精度除法,计算大整数A和普通量级的整数b的除法结果
// A从低位到高位存储,商从返回值返回,余数从形参表返回
vector<int> div(vector<int>& A, int b, int& r) {// 保存商vector<int> C;// 从A的高位到低位for (int i = A.size() - 1; i >= 0; i -- ) {// 高一位传下来的余数r,乘以10变成这一位的量级,再加上当前位的值A[i]int val = r * 10 + A[i];// 除以b的结果就放在C新的一位上C.push_back(val / b);// 除完之后,对b取模就是这一位留下的余数// 如果A没走完,就会在下一位处理;如果A走完了,就会从形参表引用返回r = val % b;}// 由于计算结果商C是从高位到低位存的,这里把它翻转成从低位向高位存的reverse(C.begin(), C.end());// 去除高位的连续0,再返回while (C.size() > 1 && C.back() == 0) C.pop_back();return C;
}int main() {// 读入大整数a和普通量级的整数bstring a;int b;cin >> a >> b;// 把A从低位到高位存储(和其它四则运算保持一致)vector<int> A;for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');// 计算A/b的结果,商为C,余数是rint r = 0;auto C = div(A, b, r);// 从高位到地位输出商C,输出余数rfor (int i = C.size() - 1; i >= 0; i -- ) cout << C[i];cout << endl << r << endl;return 0;
}

【算法学习笔记】11:高精度整数A+B、A-B、A*b、A/b相关推荐

  1. MIPS汇编语言学习笔记11:整数减法 (mult方法)

    任务:整数相乘,并输出打印. 代码: .dataintA: .word 5intB: .word 2 .textli $v0, 1lw $a1, intAlw $a2, intBmult $a1, $ ...

  2. 点云学习笔记11——VoxelNet算法+代码运行

    点云学习笔记11--VoxelNet算法+代码运行 一.算法分析 摘要 介绍 相关工作 1.2. 贡献 2.VoxelNet 2.1.特征学习网络 2.1.1 特征学习网络 二.代码复现 2.1.环境 ...

  3. 数据结构与算法 学习笔记(5):字符串

    数据结构与算法 学习笔记(5)- 字符串 本次笔记记录了LeetCode中关于字符串的一些问题,并给出了相应的思路说明和代码.题目编号与LeetCode对应,方便查找. 题目1:LeetCode 13 ...

  4. Hadoop学习笔记—11.MapReduce中的排序和分组

    Hadoop学习笔记-11.MapReduce中的排序和分组 一.写在之前的 1.1 回顾Map阶段四大步骤 首先,我们回顾一下在MapReduce中,排序和分组在哪里被执行: 从上图中可以清楚地看出 ...

  5. matlab中x从0到5不含0,关于MATLAB的数学建模算法学习笔记

    关于MATLAB的数学建模算法学习笔记 目录 线性规划中应用: (3) 非线性规划: (3) 指派问题;投资问题:(0-1问题) (3) 1)应用fmincon命令语句 (3) 2)应用指令函数:bi ...

  6. Python最优化算法学习笔记(Gurobi)

    微信公众号:数学建模与人工智能 github地址:https://github.com/QInzhengk/Math-Model-and-Machine-Learning Python最优化算法学习笔 ...

  7. LMS与RLS算法学习笔记

    LMS与RLS算法学习笔记 一. 研究目的 1.1最陡下降法理论 1.2$LMS$算法 1.3$RLS$算法 1.4研究目标 二.代码解析 三.结果 实现代码点击 这里下载 一. 研究目的 1.1最陡 ...

  8. 数学建模算法学习笔记

    数学建模算法学习笔记 作为建模Man学习数学建模时做的笔记 参考文献: <数学建模姜启源第四版> 网上搜罗来的各种资料,侵删 1.线性预测 levinson durbin算法,自相关什么的 ...

  9. 基于MVS的三维重建算法学习笔记(五)— 立体匹配经典算法PatchMatch论文翻译及要点解读

    基于MVS的三维重建算法学习笔记(五)- 立体匹配经典算法PatchMatch论文翻译及要点解读 声明 问题提出 问题建模 通过PatchMatch获取平面参数--Inference via Patc ...

  10. Python预测 数据分析与算法 学习笔记(特征工程、时间序列)

    微信公众号:数学建模与人工智能 GitHub - QInzhengk/Math-Model-and-Machine-Learning 第3章 探索规律 3.1 相关分析 相关关系是一种与函数关系相区别 ...

最新文章

  1. nginx配置websocket负载均衡
  2. 面向对象编程(OPP)
  3. pytorch 损失函数总结
  4. LaTex文章中插入Visio及Matlab矢量图
  5. pwnable.kr lotto题解
  6. linux查看磁盘挂载的三种方法
  7. 微信小程序把wx.showToast的文字提示长度增加的方法
  8. Tomcat10 开机启动 Linux环境
  9. python ks检验_python – 使用Scipy的stats.kstest模块进行拟合优度测试
  10. mysql检索面试题目_MySQL面试题目二十七道整理
  11. 使用Python可以做些什么
  12. 剑指offer面试题[14]-调整数组顺序使奇数位于偶数前面
  13. Bailian2726 采药【模拟】
  14. 不定期更新的IDEA功能整理
  15. C盘\用户目录下\管理员文件夹 如何重命名?
  16. 综合项目之闪讯破解(一)之 闪讯拨号用户名核心算法
  17. 20145222何志威《网络对抗》- Web安全基础实践
  18. 制作你软盘镜像_codestorm_新浪博客
  19. FFmpeg音视频播放器实现
  20. Unit Of Measure UOM in Oracle Applications Inventory

热门文章

  1. android 热启动闪退,由Instant Run引发的问题及其解决方案
  2. 小红书0粉店铺爆单特训营项目拆解
  3. webpack构建时堆内存溢出解决办法
  4. H5+pdfJS的实现
  5. 2023年上半年(第30批)上海市市级企业技术中心申报通知
  6. 零售行业如何开展私域流量运营?试试快鲸scrm
  7. html 下拉框设置名称,html下拉菜单怎么做?高手教你如何在HTML和CSS中创建下拉菜单...
  8. “”21天好习惯”第一期-2
  9. Spring Boot集成第三方登录之微博登录
  10. 利用openssl对base64加解密