高精度减法
- 两个高精度整数的减法
- 题目
- 思路实现
- 代码实现
- 两个任意符号的高精度加减法
两个高精度整数的减法
高精度指的是数字的大小非常非常大,最多能有10的5次方 的 位数。
本次计算的两个数均为 正数,如何求负数会在最后提到。
题目
给定两个正整数(不含前导 0 0 0),计算它们的差,计算结果可能为负数。
输入格式
共两行,每行包含一个整数。
输出格式
共一行,包含所求的差。
数据范围
1 ≤ 整数长度 ≤ 1 0 5 1 \le 整数长度 \le 10^5 1≤整数长度≤105
输入样例:
32
11
输出样例:
21
思路实现
我们先来模拟一下人类是怎么实现减法的。
首先 先计算 5 - 6,由于不够减,那么就需要 向上借一位。
然后就变成了 15 - 6,等于9。
接着计算 7 - 9,由于上一位 借了一位,所以应该是 6 - 9。
由于又不够借,所以再向上借一位。
16 - 9 = 7
最后 就是 11 - 5,所以最终答案是 679。
其实高精度减法 的 思路 是跟我们的减法是一模一样的。
代码实现
接着来看代码。
跟高精度加法一样,由于没有一个数据类型能够存储这么大的数字,所以需要用到数组存储.
由于vector容器不用手动 记录数组当前有效数据的个数,所以我们采用 vector 容器(后面也会有数组的代码,相较于vector,数组的优点是 比vector 会在时间上 快很多,vector的优点是 代码简单)。
这个字符串类型 是用于 读入 很大的数字的,因为整数类型存不下。
用字符串读入,然后遍历字符串,将每一位数字,存到数组里,这里的数字是倒着存的,原因我们后面在说。
注意这里的字符串遍历的 每一位都是 字符,所以一定要减去 ‘0’(字符0)。
接着我们需要注意一个问题,虽然读入的都是正数,但是 相减的操作是有可能 产生负数的。
根据人类减法的习惯,如果是一个小数字减去一个大数字,那么我们在脑子当中 计算的过程是,先大数字减去小数字 然后再带上负号。
所以这个时候就需要判断 A这个数字大,还是B这个数字大。
如果 A 大于等于B ,那么则正常计算。
如果 B 比较大,那么则计算 B - A,然后打印的 时候 多打印个 符号 即可。注意这里等于的情况是不需要打印 符号的,要不然结果会是 - 0。
接着我们来实现 compare 方法和 sub 方法。
对于compare方法来说,需要判断 A 数组里面存的数字大还是 B数组里面存的数字大。
规定 返回true 的时候 是 A大于等于 B ,返回 false 的时候是 B 大于 A。
注意:这里 等于这种情况一定 是 true ,因为如果是false 的 话 就会出现 -0的情况。 0是不需要加负号的。
首先 先看他们的数组长度,如果不一样,那么长度长的 一定大。
接着就开始从最高位比较,由于数组是倒着存的,那么最高位在数组的末尾。
其中 由于 此时的 A 的长度一定等于 B 的长度,所以 for循环 i 的初始值是谁的长度都是一样的。
从最高位开始比,如果两个值 不一样,那么大的整个数字 就一定大。
如果for 循环走完了,那么就说明 两个数字相等了,根据之前我们说的 ,等于一定是返回 true 的,要不然 就会打印 -0 .
当然这段代码还可以进行小优化。
这里的等号可以去掉,因为一定不可能出现等于的情况。
接着我们来看 sub 函数
首先函数的外壳长这个样子
由于 在main函数里面的 操作,这里 A的大小一定 是比 B的大。
接着需要定义一个变量来存储 上一位的借位。
这个 t 的 含义是 上一位 欠了几,比如 如果 5- 9 ,那么就叫做 欠了-1。
让这个 t 加上 被减数,然后如果 B 此时的位数有数字的话,减去 B这个数字。
接下来,就需要将结果放到 另一个vector容器中,到这里我们还没定义,得补上。
此时有两种情况,第一种是 t < 0,此时 就需要借一位了,就需要插入 (t + 10),另一种是 t > 0,此时不需要借位,直接插入 t 就可以了。
其实这两种情况也可以弄成一种情况。
如果插入 (t + 10) % 10,此时这两种情况就会都满足。
接着 再根据 t 的值,给出 这一位 欠多少。
如果小于 0 则代表,借了个1。大于0 则代表没有借。
最终经过一次遍历,得到的便是 两数相减的结果。
只不过此时还没有结束,我们少考虑了一个东西。
比如 是下面两个数 相减
那么就会得到 0025,所以我们需要把前面的0都给消去。
需要满足两个条件才抹去 最高位,第一个是 剩下的位数必须大于1,可不敢答案本来是 0,然后给他抹去了。
第二个是 你的最高位为0。
由于在数组后端移除数字比较方便,所以也解释了为什么数字在数组当中是倒着存的,当然也有一个原因是因为,要跟我们的高进度加法保持一致,这样组合运算的时候会方便很多。
最后,返回 vector容器 C即可。
完整代码如下:
#include <iostream>
#include <vector>
using namespace std;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> sub(vector<int>& A, vector<int>& B)
{vector<int> C;int t = 0;for (int i = 0; i < A.size(); i++){t += A[i];if (i < B.size()) t -= B[i];C.push_back((t + 10) % 10);if (t < 0) t = -1;else t = 0;}while (C.size() > 1 && C.back() == 0) C.pop_back();return C;
}int main()
{string a, b;vector<int> A, B;cin >> 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');if (compare(A, B)){auto C = sub(A, B);for (int i = C.size()-1; i >= 0; i--) cout << C[i];}else{auto C = sub(B, A);cout << '-';for (int i = C.size()-1; i >= 0; i--) cout << C[i];}return 0;
}
数组代码如下:
#include <iostream>
using namespace std;const int N = 1e5+10;string a, b;
int A[N], B[N], C[N], asz, bsz, csz;bool compare(int a[], int b[])
{if (asz != bsz) return asz > bsz;for (int i = asz-1; i >= 0; i--){if (a[i] != b[i]) return a[i] > b[i];}return true;
}void sub(int a[], int b[])
{int t = 0;for (int i = 0; i < asz; i++){t += a[i];if (i < bsz) t -= b[i];C[csz++] = (t + 10) % 10;if (t < 0) t = -1;else t = 0;}while (csz > 1 && C[csz-1] == 0) csz--;
}int main()
{cin >> a >> b;for (int i = a.size()-1; i >= 0; i--) A[asz++] = a[i] - '0';for (int i = b.size()-1; i >= 0; i--) B[bsz++] = b[i] - '0';if (compare(A, B)){sub(A, B);for (int i = csz-1; i >= 0; i--) cout << C[i];}else{swap(asz, bsz);sub(B, A);cout << '-';for (int i = csz-1; i >= 0; i--) cout << C[i];}return 0;
}
易错点:注意 数组的代码 中 如果 结果是负数,那么就一定要交换 asz 和 bsz 的值。
两个任意符号的高精度加减法
接下来我们来分析一下,如果题目当中给的数字 有负数怎么办,其实很好解决。
对于所有的问题 我们都可以把他们分为两种情况。
一种 是A 的绝对值加上B 的绝对值,另一种是 A的绝对值减去 B 的绝对值.
比如 -5 - 10 就可以转化为 - (5 + 10);
-5 + 10 就可以转化为 10 - 5 …
所以只需知道每个数是正的还是负的就可以了,我们在读取数字的时候是用 字符串读取的,所以是不是负数,我们只需要判断第一个字母是不是负号就可以了。
最后再根据 两个数的正负和 进行加或减的操作分类 即可。
完