目录
1、引入
在进行大整数运算中,因为在C++/C中整数,最大也就是unsigned long long也就才(1e19+8e18)位,如果要几百位的相加减就不行了,所以就要用高精度了,这里只在C++/C上使用有价值,在例如python、Java语言上无需写此算法,python可以无限大,Java里有相关库可以引入。
2、入门思路
我们如何制作一个算法,让其可以在C++/C中可以进行理论上无限位数的数进行运算,我们试想可以把数存储在数组中,然后根据我们手算的思路,操作数组来进行运算,如下图所示:
3、数的存储
这里数的存储呢需要进行逆序存储,因为逆序存储方面进位。以加法为例所示:
4、高精度加法
我们通过手算的规律可以知道,因为随时可能存在例如(9+1)这的进位,则需要设置一个变量存储是否有进位。如果有进位。那么在下一次运算的时候就加上它,所以可以归结为
t=A[i]+B[i]+t;t初始化为0。
我们算出t之后,t可能>=10,这时候我们只需要个位,然后进1,所以C[i]=t%10;
如果算出是否有进位呢?只需要用t/10判断,因为t<10的时候,t=0;
算法如下:
#include <iostream>
#include <vector>
using namespace std;
vector<int> add(vector<int> &A,vector<int> &B){
if(A.size()<B.size())return add(B,A);//大的数作为第一个参数
int t=0;
vector<int> C;
for(int i=0;i<A.size();i++){
t+=A[i];//t=A[i]+B[i]+t
if(i<B.size())t+=B[i];
C.push_back(t%10);
t/=10;
}
if(t)C.push_back(t); //如果t==1,说明有进位,因为是逆序,在结尾进位即可
return C;
}
int main(){
string a,b;//用字符串存,因为长度太长long long 存储不了
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');
//计算
vector<int> C=add(A,B);
//返回
for(int i=C.size()-1;i>=0;i--){
cout << C[i];
}
return 0;
}
5、高精度减法
这里不考虑-10-10等这些负数情况,只考虑正数
高精度减法,我们要考虑到(进1)的情况,则也需要一个t来记录是否需要进1,与高精度加法思路相同,t=A[i]-B[i]-t;但是需要注意的是这里的t可能存在<0和>=0的情况,因为我们这里无法通过代码来通俗的描述进1变十的情况,那么就只能直接A[i]-t-B[i],那么必定会出现负数情况,那该如何存储到C数组中呢,只需要对(t+10)%10,存储即可,可以进行手算验证,这里的t+10呢是防止出现负数情况,如果是正数呢,t+10然后再模10其实就回去了,没有变。如t=3,13%10=3,如果是t=-2,存入的数就为8,这里因为借了十,就加10,也可以这样理解。
那么这里t<0呢,就代表其实是需要进位的,所以就可以写t<0时,t=1;则会t=0;
当然,这个后续需要去掉前导0,因为在运算时候可能位数改变如
代码如下:
#include <iostream>
#include <vector>
using namespace std;
bool cmp(vector<int> &A,vector<int> &B){//比较A和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){
if(!cmp(A,B))cout << '-';
if(!cmp(A,B))return sub(B,A);//如果A不大于B,则转一下
vector<int> C;
for(int i=0,t=0;i<A.size();i++){
t=A[i]-t;
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');
vector<int> C;
C=sub(A,B);
for(int i=C.size()-1;i>=0;i--){
cout << C[i];
}
return 0;
}
6、高精度乘法
这里我们只考虑大数 X 小数的情况(A[i]xb),也就是b用int存储,在大多场合已经够用了
在高精度乘法中,我们可以把乘数看做一个整体依次相乘被乘数,被除也需要用t标记进位,因为当相乘后需要进行逐次相加,则相加后,我们可以取除了余数的前几项的进位,然后用在下次计算中把t再次加起来。而计算结束的时候也就是没有进位的时候。
设C=(A[i]*b+t)%10;//运算后取模,这里需要把进位也加上去
且t=(t+A[i]*b)/10;//运算后取除最后一项的前几项且加上进位并存储到t变量,为下一次做准备。
、例如:
代码:
#include<iostream>
#include<vector>
using namespace std;
//C = A * b
vector<int> mul(vector<int> &A,int b){
vector<int> C;
int t=0;//进位
for(int i=0;i<A.size() || t;i++){//当进位没有处理完就继续
if(i<A.size())t+=A[i]*b;//当A[i]没有扫完
C.push_back(t%10);
t/=10;
}
while(C.size()>1 && C.back()==0)C.pop_back();//当b是0时 可能出现多个0,这时候弹出
return C;
}
int main(){
string a;
int b;
vector<int> A;
cin >> a >> b;
for(int i=a.size()-1;i>=0;i--)A.push_back(a[i]-'0');
vector<int> C=mul(A,b);
for(int i=C.size()-1;i>=0;i--)cout << C[i];
return 0;
}
7、高精度除法
注意:除法区别于加法、减法、乘法,除法是从高位计算,这里也是用高精度x低精度
因为除法比较特殊,没有所谓什么进位什么的,因为只会越来越小,除了需要处理前导0。这里只需要一个r变量存储计算过程中的余数就能轻松写出除法代码,因为计算机总是一个数字一个数字的进行扫描,第一位不能商的情况下会商一个0,每次求出r余数的时候,因为在数字没有扫描完全部的时候,都可以进行求商(包括0),程序执行时这样的,
则r=r*10+A[i];
c=r/b//将商存入商数组。
r=r%b;//求每一步的余数
例如:
代码:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> div(vector<int> &A,int &b,int &r){
vector<int> C;
r=0;
for(int i=A.size()-1;i>=0;i--){//高位开始,区别于加减乘
r=r*10+A[i];
C.push_back(r/b);
r%=b;
}
reverse(C.begin(),C.end());//注意:算出的结果也是正序的,这里翻转是因为需要去掉前导0.且与加减乘相对应;
while(C.size()>1 && C.back()==0 )C.pop_back();
return C;
}
int main(){
string a;
int b,r;
vector<int> A;
cin >> a >> b;
for(int i=a.size()-1;i>=0;i--)A.push_back(a[i]-'0');
vector<int> C=div(A,b,r);
for(int i=C.size()-1;i>=0;i--)cout << C[i];
cout << endl << r;
}