高精度加法
对于给定的两个特别大的数我们用两个字符串来接收 s1
和s2
。
例如:对于两个数 56215455
和95425453
,即 s1 = "56215455" , s2 = "95425453"
。
对于这两个数,分别用两个列表 a
和b
来接收(例如:C++ 用 vector
,Java 用 List
)它们。
对于上面的例子: a = {5,6,2,1,5,4,5,5} , b = {9,5,4,2,5,4,5,3}
接着对这两个数模拟 竖式计算。
从 a
和b
的最后一位数开始向前模拟计算。
代码:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
//用来接收两个数的
string s1,s2;
vector<int> add(vector<int>& a,vector<int>& b){
int m = a.size(),n = b.size();
vector<int> c;
//分别从 a 和 b 的最后一位开始模拟
//ca 是进位,初始进位为0
int i = m - 1,j = n - 1,ca = 0;
//a 没计算完 ,b 没计算完,ca 进位是1 就继续模拟
while(i >= 0 || j >= 0 || ca){
//sum 是需要加上每次的进位的,这里直接初始化为 ca
int sum = ca;
//如果 a 没计算完,就继续计算,否则忽略
if(i >= 0){
sum += s1[i] - '0';
i--;
}
//如果 b 没计算完,就继续计算,否则忽略
if(j >= 0){
sum += s2[j] - '0';
j--;
}
//sum / 10 的余数就是我们已经计算得出的结果 记录下来
c.push_back(sum % 10);
//更新进位
ca = sum / 10;
}
//因为我们是从 a 和 b 的低位开始模拟的,答案也是先记录的低位和 所以需要反转过来
//例如 a = {1,2,3} b = {4,5,6}
// c = {9,7,5} 正确答案应该是 579 需要反转一下
reverse(c.begin(),c.end());
return c;
}
int main(){
//接收两个数
getline(cin,s1);
getline(cin,s2);
//a 和 b 分别是装两个数的容器
vector<int> a,b;
int m = s1.size(),n = s2.size();
for(int i = 0;i < m;i++) a.push_back(s1[i] - '0');
for(int i = 0;i < n;i++) b.push_back(s2[i] - '0');
//c就是装有结果的容器
auto c = add(a,b);
for(auto x:c) printf("%d",x);
return 0;
}
高精度减法
我们继续分别用两个列表 a
和b
来接收 被减数 和 减数。
模拟减法的过程,先是
B
0
−
A
0
B_0 - A_0
B0−A0,有两种情况:
- 如果 B 0 − A 0 > = 0 B_0 - A_0 >= 0 B0−A0>=0,就不用管,直接减
- 如果
B
0
−
A
0
<
0
B_0 - A_0 < 0
B0−A0<0,那么
B
0
B_0
B0就不够减,需要向
B
1
B_1
B1借位,此时就是
10
+
B
0
−
A
0
10 + B_0 - A_0
10+B0−A0,借位之后
B
1
B_1
B1 也要减去借位
1
。
我们用 t
表示借位。
此外,高精度减法 与 高精度加法相比,还有两个需要注意的点:
-
我们要保证是 大数 减 小数,因为我们要保证是 数位多的 减 数位少的。
-
- 如果
a
−
b
>
=
0
a - b >= 0
a−b>=0,那么
a-b
。
- 如果
a
−
b
>
=
0
a - b >= 0
a−b>=0,那么
-
- 如果
a
−
b
<
0
a - b < 0
a−b<0,那么
b-a
,最后在答案前面加一个-
号。
- 如果
a
−
b
<
0
a - b < 0
a−b<0,那么
-
要去掉多余的前导
0
。(例如:a = 1234 , b = 1230
,那么a - b = 4
,实际上我们存储答案的时候 是倒着存的,把0
也存了进去,实际上存的是4000
,所以需要把这些0
去掉)。
代码:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
string s1,s2;
//判断 a 是否大于等于 b
bool cmp(vector<int>& a,vector<int>& b){
if(a.size() != b.size()) return a.size() > b.size();
for(int i = 0;i < a.size();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 m = a.size() , n = b.size();
for(int i = m - 1,j = n - 1,t = 0;i >= 0;i--){
int sum = a[i] - t;
if(j >= 0){
sum -= b[j];
j--;
}
//包含两种情况
//1. sum = a[i] - b[i] - t < 0 , 这时就需要借位 (10 + sum) % 10 就等于 sum + 10
//2. sum = a[i] - b[i] - t >= 0, 这时直接减 , (sum + 10) % 10 还是等于 sum
c.push_back((sum + 10) % 10);
//判断是否要借位,sum < 0,说明不够减 才需要借位
if(sum < 0) t = 1;
else t = 0;
}
//去除多余的前导0
while(c.size() > 1 && c.back() == 0) c.pop_back();
//将答案翻转过来
reverse(c.begin(),c.end());
return c;
}
int main(){
getline(cin,s1);
getline(cin,s2);
vector<int> a,b;
int m = s1.size() , n = s2.size();
for(int i = 0;i < m;i++) a.push_back(s1[i] - '0');
for(int i = 0;i < n;i++) b.push_back(s2[i] - '0');
//如果 a >= b , 直接 a - b
if(cmp(a,b)){
auto c = sub(a,b);
for(auto x:c) printf("%d",x);
}
//如果 a < b , 直接 b - a , 在答案前面添加 一个 - 号
else{
auto c = sub(b,a);
printf("-");
for(auto x:c) printf("%d",x);
}
return 0;
}
高精度乘法
我们继续分别用两个列表 a
和b
来接收两个数。
我们已知 两个数的乘积的长度 一定不大于 两个数分别的长度。例如,两个数 ab
和cdef
的乘积的长度一定不大于 6
。
我们令 m = a.size() , n = b.size()
,对于存放两个数乘积的 c
的size
应该是 m+n
。
我们定义 c[k]
是所有 a[i] * b[j]
(i + j == k
)的和。
接着我们再处理 c
,把里面的每一个数都放在合适的位置。
同时也要注意处理前导零。(因为存在 a == 0
或者 b==0
的情况)。
代码:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
string s1, s2;
vector<int> mul(vector<int>& a, vector<int>& b) {
int m = a.size() , n = b.size();
vector<int> c(m + n);
//c[i+j] 等于 所有 a[i] * b[j]的和
//为了方便处理 这里就没有逆序处理
//如果 s1 = 123 s2 = 456
//那么 a = 321 b = 654
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
c[i + j] += a[i] * b[j];
}
}
//处理 c
for (int i = 0, ca = 0; i < c.size() || ca; i++) {
ca += c[i];
if (i < c.size()) c[i] = ca % 10;
else c.push_back(ca % 10);
ca /= 10;
}
//去除前导0
while (c.size() > 1 && c.back() == 0) c.pop_back();
reverse(c.begin(), c.end());
return c;
}
int main() {
getline(cin, s1);
getline(cin, s2);
vector<int> a, b;
int m = s1.size() , n = s2.size();
//为了方便处理 这里从低位到高位存储
for (int i = m - 1; i >= 0; i--) a.push_back(s1[i] - '0');
for (int i = n - 1; i >= 0; i--) b.push_back(s2[i] - '0');
auto c = mul(a, b);
for (auto x : c) {
printf("%d", x);
}
return 0;
}
高精度除法
这里的高精度除法 是一个 非常大的数 a
➗一个较小的数 b
。
跟其他几个不同的是,除法 是从高位向低位开始➗的。
例如,123 ➗ 45
,是从a
的最高位 1
开始➗的。
在模拟除法的时候,需要用余数 r
来参与到下一位数的除法。
高精度除法也需要去除前导0
。例如,100 ➗ 9
商是 011
,余数是 1
,商需要去掉前面的 0
,所以应该是 11
。
代码:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> division(vector<int>& a,int b,int& r){
vector<int> c;
int m = a.size();
//从高位开始除
for(int i = 0;i < m;i++){
r = r * 10 + a[i];
c.push_back(r / b);
r = r % b;
}
//123 ➗ 13 商是 09 余数r = 6
//所以我们需要先把 c 翻转过来,方便我们去除前导0 ,翻转过来后为 90
reverse(c.begin(),c.end());
//取除前导0
while(c.size() > 1 && c.back() == 0) c.pop_back();
//去除完毕之后,再将商翻转回来
reverse(c.begin(),c.end());
return c;
}
int main(){
string s1;
int b;
getline(cin,s1);
cin>>b;
vector<int> a;
int m = s1.size();
for(int i = 0;i < m;i++) a.push_back(s1[i] - '0');
//r 是余数
int r = 0;
auto c = division(a,b,r);
for(auto x:c) printf("%d",x);
//先换行 再输出余数 r
cout<<endl<<r<<endl;
}