参考:加减乘除
手动模拟小学生列竖式
一、大数加法(都是正数时)
用两个数组分别逆序存储输入的数,每个位都分别相加,判断是否进位
代码:
#include<stdio.h>//大数加法
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
const int maxn = 1e4+7;
#define Max(a,b) a>b?a:b
char str1[maxn],str2[maxn];
int a[maxn],b[maxn];
void strv(char str[],int a[],int n)//用数组逆序存储
{
for(int i=0,j=n-1;i<n;i++,j--)
{
a[j]=str[i]-'0';
}
}
int main()
{
while(~scanf("%s%s",str1,str2))
{
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
int n1=strlen(str1);
int n2=strlen(str2);
strv(str1,a,n1);
strv(str2,b,n2);
int i,w;
int k=Max(n1,n2);
for(i=0,w=0;i<k;i++){
a[i]=a[i]+b[i]+w;
w=a[i]/10;
a[i]%=10;
}
if(w)
a[k]+=w;
for(int i=k;i>=0;i--)//相加后的和,可能比最大的数还要多一位
{
if(i==k&&a[k]==0)
continue;
printf("%d",a[i]);
}
printf("\n");
}
return 0;
}
大数加法(可能为负数时)
参考:博客链接
方法:
第一步:将字符串转化为数组存储,若为负数,就把所有位的数都变成负的存进数组(不要符号位)
第二步:对应位相加,并把结果保存在c数组里,注意进位
第三步:判断最高位正负,最高位为正,则最终结果一定为正;
最高位为负,最终答案一定为负,这时,提出来一个负号,也就是先输出一个负号,然后c[i] = -c[i]
第四步:从个位开始,小于0的向前借一位
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
const int maxn=1e4+7;
char s1[maxn],s2[maxn];
int a[maxn],b[maxn],c[maxn];
int deal(char str[],int a[])//第1步 对输入的数进行处理
{
int n=strlen(str);
if(str[0]=='-')//若为负数,就把所有位的数都变成负的存进数组(不要符号位)
{
for(int i=0;i<n-1;i++)
{
a[i]=str[i+1]-'0';
a[i]=-a[i];
}
n--;
}
else
{
for(int i=0;i<n;i++)
{
a[i]=str[i]-'0';
}
}
return n;
}
int main()
{
while(~scanf("%s%s",s1,s2))
{
int len1=deal(s1,a);
int len2=deal(s2,b);
int up=0,k=0,i,j;
///从后向前对应位相加,把个位保存在c数组的第一位
for(i=len1-1,j=len2-1;i>=0&&j>=0;i--,j--)//第2步
{//先处理相同位数部分
c[k]=(a[i]+b[j]+up)%10;
up=(a[i]+b[j]+up)/10;
k++;
}
//处理多出来的位数
if(len1>len2)
{
while(i>=0)
{
c[k]=(a[i]+up)%10;
up=(a[i]+up)/10;
k++;
i--;
}
}
if(len1<len2)
{
while (j >= 0)
{
c[k]=(b[j]+up)%10;
up=(b[j]+up)/10;
k++;
j--;
}
}
//下面5行是(都为正数时)相加后比输入时(最大的位数)多一位的情况
if(up!=0)
{
c[k]=up;
k++;
}
//***
if(c[k-1]<0)//第3步
{//判断最高位的正负 ,最高位为负,最终答案一定为负
for(int i=0;i<k;i++)
c[i]=-c[i];
printf("-");
}
for(int i=0;i<k;i++)//第4步
{//从个位开始,小于0的向前借一位
if(c[i]<0)
{
c[i]+=10;
c[i+1]--;
}
}
if(k==1&&c[0]==0)
printf("0\n");
else
{
while(!c[k-1])//前导清0
k--;
for(int i=k-1;i>=0;i--)
printf("%d",c[i]);
printf("\n");
}
}
return 0;
}
二、大数减法
主要是使前一个数比后一个数大,且逆序后,让小的那个数后面补0保证两个数位数相同,最后在相减(别忘了去除前导0)
代码:
#include<stdio.h>//大数减法
#include<string.h>
#include<math.h>
#include<algorithm>
#define Max(a,b) a>b?a:b
using namespace std;
const int maxn=1e3+7;
char aa[maxn],bb[maxn],aacopy[maxn],bbcopy[maxn];
int a[maxn],b[maxn];
int main()
{
while(~scanf("%s%s",aa,bb))
{
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
int flag=1;//正负号
int len1=strlen(aa);
int len2=strlen(bb);
for(int i=0;i<len1;i++)
aacopy[i]=aa[i];
for(int i=0;i<len2;i++)
bbcopy[i]=bb[i];
//将字符串转化为整数存入数组 且总是第一个数大于第二个数
//b存小的数,a存大的数
if(len1<len2)
{
for(int i=0;i<len1;i++)
b[i]=aacopy[len1-i-1]-'0';
for(int i=len1;i<len2;i++)//中间差的位数用0补上
b[i]=0;
for(int i=0;i<len2;i++)
a[i]=bbcopy[len2-i-1]-'0';
flag=0;
}
else if(len1>len2)
{
for(int i=0;i<len1;i++)//用数组逆序存储
a[i]=aacopy[len1-i-1]-'0';
for(int i=0;i<len2;i++)
b[i]=bbcopy[len2-i-1]-'0';
for(int i=len2;i<len1;i++)
b[i]=0;
}
else if(len1==len2)
{
if(aa>=bb)
{
for(int i=0;i<len1;i++)
a[i]=aacopy[len1-i-1]-'0';
for(int i=0;i<len2;i++)
b[i]=bbcopy[len2-i-1]-'0';
}
else
{
for(int i=0;i<len1;i++)
a[i]=bbcopy[len1-i-1]-'0';
for(int i=0;i<len2;i++)
b[i]=aacopy[len2-i-1]-'0';
flag=0;
}
}
int len=Max(len1,len2);
if(flag==0)
printf("-");
//大数相减
int c[maxn];
for(int i=0;i<len;i++)
{
c[i]=a[i]-b[i];
if(c[i]<0)
{
c[i]=c[i]+10;
a[i+1]=a[i+1]-1;
}
}
//去除前导0
while(c[len-1]==0&&len!=1)
{
len--;
}
for(int i=len-1;i>=0;i--)
printf("%d",c[i]);
printf("\n");
}
return 0;
}
三、大数乘法
(这里写的大数乘法和除法只适用于输入数长度不是特别大时,如:链接:大数乘法v2 大数除法v2
A,B的长度 <= 100000,A,B >= 0 此时用此方法会超时,特别大的具体算法,之后再更新。)
规律: 即一个数的第i 位和另一个数的第j 位相乘所得的数,一定是要累加到结果的第i+j 位上。这里i, j 都是从右往左,从0 开始数。
ans[i+j] = a[i]*b[j];
7 8 9 6 5 2
× 3 2 1 1
-----------------
7 8 9 6 5 2 <---- 第1趟
7 8 9 6 5 2 <---- 第2趟
.......... <---- 第n趟
-----------------
? ? ? ? ? ? ? ? <---- 最后的值用另一个数组表示
例题:1027 大数乘法
代码:
#include<stdio.h>//大数乘法
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
const int N=1e3+7;
char str1[N],str2[N];
int a[N],b[N],c[2*N];
void strv(char str[],int a[],int n)//用数组逆序存储
{
for(int i=0,j=n-1;i<n;i++,j--)
{
a[j]=str[i]-'0';
}
}
int main()
{
while(~scanf("%s%s",str1,str2))
{
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
if(str1[0]=='0'||str2[0]=='0')
printf("0\n");
else
{
int n1=strlen(str1);
int n2=strlen(str2);
strv(str1,a,n1);
strv(str2,b,n2);
//模拟每一位的竖式计算过程(从个位开始)
for(int i=0;i<n1;i++)
{
for(int j=0;j<n2;j++)
{
c[i+j]=a[i]*b[j]+c[i+j];//按位进行乘法
c[i+j+1]=c[i+j+1]+c[i+j]/10;//进位
c[i+j]=c[i+j]%10;
}
}
int n=n1+n2;
while(!c[n])//前导清0
n--;
for(int i=n;i>=0;i--)
printf("%d",c[i]);
printf("\n");
}
}
return 0;
}
四、大数除法
我看人家的代码写的特别详细,就直接粘别人的代码了,原博客链接在最上面。
#include<stdio.h>
#include<string.h>
char a[100],b[100];//用两个字符串用来输入两个大数
int x[100],y[100],z[100],m[100];//被除数 除数 商 余数
int digit;//大数的位数
void sub(int x[],int y[],int len1,int len2)//大数减法
{
int i;
for(i=0;i<len1;i++)
{
if(x[i]<y[i])
{
x[i]=x[i]+10-y[i];
x[i+1]--;
}
else
x[i]=x[i]-y[i];
}
for(i=len1-1;i>=0;i--)//判断减法结束之后,被除数的位数
{
if(x[i])
{
digit=i+1;
break;
}
}
}
int judge(int x[],int y[],int len1,int len2)
{
int i;
if(len1<len2)
return -1;
if(len1==len2)//若两个数位数相等
{
for(i=len1-1;i>=0;i--)
{
if(x[i]==y[i])//对应位的数相等
continue;
if(x[i]>y[i])//被除数 大于 除数,返回值为1
return 1;
if(x[i]<y[i])//被除数 小于 除数,返回值为-1
return -1;
}
return 0;//被除数 等于 除数,返回值为0
}
}
int main()
{
int i,j=0,k=0,temp;
int len1,len2,len;//len两个大数位数的差值
while(~scanf("%s %s",a,b))
{
len1=strlen(a);//被除数位数
len2=strlen(b);//除数位数
for(i=len1-1,j=0;i>=0;i--)//将字符串中各个元素倒序储存在数组中
x[j++]=a[i]-'0';
for(i=len2-1,k=0;i>=0;i--)
y[k++]=b[i]-'0';
if(len1<len2)//当被除数位数 小于 除数位数时
{
printf("商是:0\n");
printf("余数是:");
puts(a);
}
else //当被除数位数 大于或者 除数位数时
{
len=len1-len2;//两个大数位数的差值
for(i=len1-1;i>=0;i--)//将除数后补零,使得两个大数位数相同。被除数:4541543329 除数:98745,加零后:9874500000
{
if(i>=len)
y[i]=y[i-len];
else
y[i]=0;
}
len2=len1;//将两个大数数位相同
digit=len1; //将原被除数位数赋值给digit
for(j=0;j<=len;j++)
{
z[len-j]=0;
while(((temp=judge(x,y,len1,len2))>=0)&&digit>=k)//判断两个数之间的关系以及位数与除数原位数的关系
{
sub(x,y,len1,len2); //大数减法函数
z[len-j]++;//储存商的每一位
len1=digit;//重新修改被除数的长度
if(len1<len2&&y[len2-1]==0)
len2=len1;//将len1长度赋给len2;
}
if(temp<0)//若被除数 小于 除数,除数减小一位。例如:被除数:4541543329 除数:(原)98745,(加零后)9874500000,后退一位后:0987450000
{
for(i=1;i<len2;i++)
y[i-1]=y[i];
y[i-1]=0;
if(len1<len2)
len2--;
}
}
printf("商是:");
for(i=len;i>0;i--)//去掉前缀0
{
if(z[i])
break;
}
for(;i>=0;i--)
printf("%d",z[i]);
printf("\n");
printf("余数是:");
for(i=len1;i>0;i--)
{
if(x[i])
break;
}
for(;i>=0;i--)
printf("%d",x[i]);
printf("\n");
}
}
return 0;
}