【算法】高精度算法:加减乘除(全)

看的视频在这里。
题目:
加法
减法
乘法
除法:高/低

加法

思想:用数组模拟高精度。
在这里插入图片描述

算法核心:

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;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

karshey

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值