加法
思想:用数组模拟高精度。
算法核心:
c[i]+=a[i]+b[i];
c[i+1]=c[i]/10;
c[i]=c[i]%10;
注意:是c[i]+=a[i]+b[i],是累加。
例题:求a+b, a、b范围都<=10^500;
注意:
1、将字符串转成数组模拟大数时要将字符转置,这样才方便做加法,因为数学是向右对齐做加法的,而数组是从左到右的。如:
1234+789==1234+0789 对齐相加,而不是1234+7890对齐相加。
故转置即向右对齐,这样进位就向左(向小的位置)进位。
2、la-i最大是la,最小是1.故后面的核心算法是从1开始到lc;
代码如下(有注释):
#include<bits/stdc++.h>
using namespace std;
char s1[505],s2[505];
int a[505],b[505],c[505];
int main()
{
//用数组模拟高精度算法
int la,lb,lc;
cin>>s1>>s2;
//得知长度
la=strlen(s1);
lb=strlen(s2);
//将字符转为数字,并将字符转置便于计算
//此时数字是倒过来的,因为数字的加法是从低位向高位相加(向右对齐)
for(int i=0;i<la;i++)
{
a[la-i]=s1[i]-'0';
}
for(int i=0;i<lb;i++)
{
b[lb-i]=s2[i]-'0';
}
//最高位数
lc=max(la,lb)+1;
//核心步骤
for(int i=1;i<=lc;i++) //为什么从1开始?因为上面的la-i最小就是1
{
c[i]+=a[i]+b[i];
c[i+1]=c[i]/10;
c[i]=c[i]%10;
}
//删除前导0
if(c[lc]==0&&lc>0) lc--;
for(int i=lc;i>0;i--)
{
cout<<c[i];
}
return 0;
}
减法
算法核心:
1、若a<b,则交换a与b,并在结果处加上负号。
2、如果a[i]<b[i],则需要高位借位。
核心代码:
if(a[i]<b[i])
{
a[i+1]--;
a[i]=a[i]+10;
}
c[i]=a[i]-b[i];
高精度减法例题的代码:
#include<bits/stdc++.h>
using namespace std;
bool compare(char s1[],char s2[])//如果s1>=s2,返回true,否则false;
{
int u=strlen(s1),v=strlen(s2);
if(u>v) return true;
if(u!=v) return u>v; //若u>v,则返回了1,否则0,符合题意
//判断u==v的情况
for(int i=0;i<u;i++)
{
if(s1[i]!=s2[i]) return s1[i]>s2[i];//这里比较的是从第一位开始的数字;
}
//若还能到这里,则两数相等
return 1;
}
char s1[100000],s2[100000];
int a[100000],b[100000],c[100000];
int main()
{
int la,lb,lc,flag=0;
cin>>s1>>s2;
if(!compare(s1,s2))//若s1<s2,则flag=1,交换两数字
{
flag=1;
char s3[10000];
strcpy(s3,s1);
strcpy(s1,s2);
strcpy(s2,s3);
}
//到这里s1>=s2了
la=strlen(s1);
lb=strlen(s2);
//转化数字并转置,因减法要向右对齐,而转置后向左对齐,正是数组从左到右的顺序
for(int i=0;i<la;i++)
{
a[la-i]=s1[i]-'0';
}
for(int i=0;i<lb;i++)
{
b[lb-i]=s2[i]-'0';
}
lc=max(la,lb);//做减法不会进位,不必加1
//由上面的减法可知,数组最小从1开始
for(int i=1;i<=lc;i++)
{
if(a[i]<b[i])
{
a[i+1]--;
a[i]=a[i]+10;
}
c[i]=a[i]-b[i];
}
//删去前导零,因为减法可能有很多零,要用循环
while(c[lc]==0&&lc>1)
{
lc--;
}
if(flag==1) cout<<"-";
//反着输出回来
for(int i=lc;i>0;i--)
{
cout<<c[i];
}
return 0;
}
减法跟加法有许多相似之处。
乘法
一个例子:
核心算法:
c[i+j-1]=c[i+j-1]+a[i]*b[j];
c[i+j]=c[i+j]+c[i+j-1]/10;
c[i+j-1]=c[i+j-1]%10;
注意,乘法的最大位数是两个乘数之和。
但是!
若是有一个乘数是0,则用if删去前导零会有很多0,如:
0*99999999999999=0000000000000
所以还是要用while删除前导0,或者特判。
代码如下:
#include<bits/stdc++.h>
using namespace std;
char s1[10000],s2[10000];
int a[10000],b[10000],c[10000];
int main()
{
int la,lb,lc;
cin>>s1>>s2;
la=strlen(s1);
lb=strlen(s2);
for(int i=0;i<la;i++)
{
a[la-i]=s1[i]-'0';
}
for(int i=0;i<lb;i++)
{
b[lb-i]=s2[i]-'0';
}
//乘法的位数
lc=la+lb;
//核心算法
for(int i=1;i<=la;i++)
{
for(int j=1;j<=lb;j++)
{
c[i+j-1]+=a[i]*b[j];
c[i+j]+=c[i+j-1]/10;
c[i+j-1]%=10;
}
}
//本来lc最多只会比答案多1位,用if删去前导0即可
//但是!要注意,若有一个乘数是0,则会有很多0,则用while稳妥
while(c[lc]==0&&lc>1) lc--;
for(int i=lc;i>0;i--)
{
cout<<c[i];
}
return 0;
}
重点是找到a[i] b[i]和c[i] 的规律。
除法
高精度除以低精度
例子:
核心代码:
for(int i=1;i<=la;i++)
{
c[i]=(x*10+a[i])/b;
x=(x*10+a[i])%b;
}
注意:这里删除前导零的方法是:
若前面有0,lc就加1,输出的时候从lc开始,这样就把前导零跳过了。
整体代码:
#include<bits/stdc++.h>
using namespace std;
char s1[5005];
long long int b,c[5005],x,a[5005],la,lc;
int main()
{
cin>>s1>>b;
la=strlen(s1);
//将被除数放入数组
for(int i=1;i<=la;i++)
{
a[i]=s1[i-1]-'0';
}
//核心算法
for(int i=1;i<=la;i++)
{
c[i]=(x*10+a[i])/b;
x=(x*10+a[i])%b;
}
lc=1;
while(c[lc]==0&&lc<la) lc++;//删除前导零
for(int i=lc;i<=la;i++)
{
cout<<c[i];
}
return 0;
}
高精度除以高精度
例子:
除数是高精度不能逐位试商,因为可能很多都试不出来。故用减法模拟除法。
注意:
1、答案的位数lc=la-lb+1;
如,20/4=5,lc是2(左边第一个是1噢)。
2、最高位的商的来源:用高精度减法,能减多少次商就是几。
3、后面位数的商:被除数是把前面余数*10加上后一位,用高精度减法,重复注意2的步骤。
一个例子:531518/123,第3位的商应该是这样求:
531518-123000,可以减几次,该位数就是几。即,除数左移3位,使得被除数与除数位数相同。
减完之后的39518/123,就是39518-12300能进行几次,即,除数左移两位。
此代码会有很多函数,反正就是让我自己写也写不出来…
代码:
#include<bits/stdc++.h>
using namespace std;
char s1[5005],s2[5005];
int a[305],b[305],c[305],tmp[305];// a/b==c
//输入字符串并将其转置,变成数字的函数
void init(int *x)
{
char s[305];
cin>>s;
x[0]=strlen(s);
//将字符串倒序转换为数字:因为涉及到了减法
for(int i=0;i<x[0];i++)
{
x[x[0]-i]=s[i]-'0';
}
}
//将除数与被除数对齐,用于做减法的函数:如将123变成12300
void numcpy(int p[],int q[],int n)
{
for(int i=1;i<=p[0];i++)
{
q[i+n-1]=p[i];//将数字往后推,前面就是0了,转过来就是对齐了的123000(例子)
}
q[0]=p[0]+n-1;
}
//比较两个数谁大的函数
int compare(int a[],int b[])//返回1:a>b;0:a==b;-1:a<b
{
if(a[0]>b[0]) return 1;
if(a[0]<b[0]) return -1;
//走到这里就是位数一样,再往前一位一位地比较
for(int i=a[0];i>0;i--)
{
if(a[i]>b[i]) return 1;
if(a[i]<b[i]) return -1;
}
//a==b
return 0;
}
//相减的函数
void minu(int a[],int b[])
{
for(int i=1;i<=a[0];i++)
{
if(a[i]<b[i])
{
a[i+1]--;
a[i]=a[i]+10;
}
//a[i]是结果了
a[i]=a[i]-b[i];
while(a[a[0]]==0&&a[0]>0) a[0]--;
}
}
//输出答案函数:转置
void print(int a[])
{
if(a[0]==0)
{
cout<<0;
return;
}
for(int i=a[0];i>=1;i--)
{
cout<<a[i];
}
return;
}
int main()
{
init(a);
init(b);
//因为数组都是从1开始存的,故a[0]等是没有用的,故将数字的位数存入a[0]
//作用是:控制商的位数
c[0]=a[0]-b[0]+1;
//核心代码:
for(int i=c[0];i>=1;i--)
{
memset(tmp,0,sizeof(tmp));
numcpy(b,tmp,i);
//以减法做除法的核心代码
while(compare(a,tmp)>=0)
{
c[i]++;
minu(a,tmp);
}
}
//删去前导零
while(c[c[0]]==0&&c[0]>0) c[0]--;
//输出商
print(c);
cout<<endl;
//输出余数(a剩下的除不了的就是余数~)
print(a);
return 0;
}