目录
一.产生原因
二.不同方法介绍
三.模拟过程(核心代码)
1.加法(进位)
2.减法(借位)
3.乘法(高精度a*低精度b)
4.除法(高精度a÷低精度b)
四.完整代码
1.加法
2.减法
3.乘法
4.除法
首先明确为什么有高精度运算即大数的运算。由于C++中最大的long long也只能存到2^63-1=9223372036854775807(19位数),但当数字长度接近10^5甚至更长时就没办法用long long储存了,所以不能直接对变量进行加减,因此有了高精度算法,最近我刚学,怕忘就总结了一下,仅供参考,如有错误还请指正。
对了:题目指路
加法:https://www.acwing.com/problem/content/793/
减法:https://www.acwing.com/problem/content/794/
乘法:https://www.acwing.com/problem/content/795/
除法:https://www.acwing.com/problem/content/796/
高精度算法一般有两种形式,①数组模拟 ②用STL中的vector容器 。
①:将字符串string中的每一位转化为int数组中的数;
②:用vector中
的函数push_back(); pop_back(); back(); front(); begin(); 进行操作;
两者的模拟加法的大致思想是一样的,但STL容器可以在空间上随用随申请,而数组只能提前申请(a[100010])固定空间。
这个是平时咱们计算的过程(红1表示进位)而写程序时咱们需要找到一个循环起来的方法,于是继续思考我们为什么会知道进位以及进多少
上图为直接对每一位相加之后的结果,从最低位(个位)看起,就可以发现8=18%10,9=(18+1)%10,1=(0+1)%10。这样就找到规律了:
每一位上的数字=(进位+加数上同位两数字的和)%10
进位=(进位+加数上同位两数字的和)/10
注:这里是默认的a的长度>b的长度!
(数组模拟,rr[]为结果)
for(int i=0; i=10) rr[i+1]++,rr[i]-=10;
}
if(rr[len]) len++;//这里处理a+b后最高位的进位问题
注:这里一定要分两个for来加和判断,要不然循环第二次进行时会覆盖++,进位就没有用到!
等等,写到这里我突然意识到只要把res那改成+=就可以了!妙蛙!
for(int i=0; i=10) rr[i+1]++,rr[i]-=10;
}
(vector)
for(int i=0; i
从结果上来看a-b可能是正数可能是负数,负数时加上符号变成b-a即可,这里不再赘述。
比较简单的情况是被减数的每一位都恰好比减数的每一位大,这里就不列举了;
比较复杂的就是下图这样有借位的情况:
还是和加法一样先对每一位做减法(红字),然后从低位(个位)开始考虑借位变成下图(蓝字)
就可以找到规律:
每一位上的数字=同位减法,判断是否<0,小于则下一位(高位)--,本位+=10
Or:
每位数字=(同位减法+10-标记)%10,如果减法<0则标记1,否则标记0
(这个标记是在下一位(高位)时才开始起作用,模拟借位的过程,先判断有没有被借过位)
注:这里默认a>b!
(数组)
for(int i=0; i
有了加法的启示这里也可以改良一下
for(int i=0; i
(vector)
for(int i=0; i
数组模拟法的难点在于一个公式的理解:
res[i+j]=a[i]*b[j]
这里我想了一个神奇的例子,希望可以帮助理解:
结果中res[7]=6,乘数中a[2]=2,b[5]=3,从这里我们可以看出res[2+5]=a[2]*b[5],规律立现!
容器法就相对简单一点,直接暴力每一位都乘一遍b,取模作为这一位上的结果,其实和加法类似。
每一位数字=(a[i]*b+jw)%10;
进位+=(a[i]*b+jw)/10;
(数组)
for(int i=0; i
(vector)
vec mul(vec &a,int b)
{
vec res;
int t=0;
for(int i=0; i
首先1/31=0,
(1*10+2)/31=0,((1*10+2)*10+0)/31=3 ,得到余数27,模拟完过程就可以找到规律了!
每位数字=(上一位的余数*10+本位)/除数
余数=(上一位的余数*10+本位)-除数*结果位数字
由于除法的力量过于玄学,此处就只放vector版本了。
r=0;//余数要有初始值0
for(int i=a.size()-1; i>=0; i--)//从高位除起,所以后续不需要reverse
{
r=r*10+a[i];
res.push_back(r/b);
r%=b;
}
篇幅有限,我就不介绍一些细节比如去除前导0了,我会在代码中注释一下。
#include
#include
#define N 10010
using namespace std;
char a[N],b[N],res[N];
int aa[N],bb[N],rr[N];
int lena,lenb;
int main()
{
scanf("%s",a);
scanf("%s",b);
int lena=strlen(a),lenb=strlen(b);
int len=max(lena,lenb);
for(int i=0; i=10) rr[i+1]++,rr[i]-=10;
}
if(rr[len]) len++;//多进一位的情况,比如99+1=100
for(int i=len-1; i>=0; i--) printf("%d",rr[i]);
}
#include
#include
#define N 10010
using namespace std;
char a[N],b[N];
int aa[N],bb[N],rr[N];
int lena,lenb;
bool flag=true;
int cmp()//a>b->1 a-1 a==b->0
{
lena=strlen(a);
lenb=strlen(b);
if(lena>lenb) return 1;
else if(lenaa-b
for(int i=0; i=0; i--) printf("%d",rr[i]);
}
#include
#include
#include
using namespace std;
typedef vector vec;
vec mul(vec &a,int b)
{
vec res;
int t=0;
for(int i=0; i1 && res.back()==0) res.pop_back();//去除前导0(现在还在0最后面)
reverse(res.begin(),res.end());
return res;
}
int main()
{
string a;
int b;
vec aa,rr;
cin>>a>>b;
for(int i=a.size()-1; i>=0; i--) aa.push_back(a[i]-'0');
rr=mul(aa,b);
for(int i=0; i
#include
#include
#include
using namespace std;
typedef vector vec;
int r;
vec div(vec &a,int b)
{
vec res;
r=0;
for(int i=a.size()-1; i>=0; i--)//从高位除起,所以后续不需要reverse
{
r=r*10+a[i];
res.push_back(r/b);
r%=b;
}
while(res.size()>1 && res.front()==0) res.erase(res.begin());//delete 0
return res;
}
int main()
{
string a;
int b;
vec aa,rr;
cin>>a>>b;
for(int i=a.size()-1; i>=0; i--) aa.push_back(a[i]-'0');
rr=div(aa,b);
for(int i=0; i