C++高精度算法

C++高精度算法

由于受到计算机硬件等因素的限制,计算机计算结果的精度也受到一定的限制,即使用Extended或comp数据类型,计算结果也最多能精确到20位,
另一方面,计算机所能表示的数的范围也受到一定的限制,如实数所能处理的最大值为1.7*1038,在计算N!时,当N=34时,计算结果就超过这个范围,无法计算。

高精度的读入和存储

如果要处理的数据超过了计算机允许的显示精度范围,读入数据时不能用数值型变量读入数据,如果读入的数据位数小于或等于255位,可用字符串变量读入数据,如果超过255位,则要用数组变量逐位读入。如果用字符串变量读入数据时,读入数据后通常将它各位数字分离,用数组将各位数字存储。我们把数92345678919放在数组a中,在数组a 中的位置如下:


读入和输出

void read(int a[]){
	string s;
	cin>>s;
	a[0]=s.size();
	for(int i=0;i<a[0];i++){
		a[a[0]-i]=s[i]-'0';
	}
}
void write(int a[]){
	for(int i=a[0];i>=1;i--){
		cout<<a[i];
	}
	cout<<endl;
}


高精度加法

输入两个正整数,求它们的和。

分析:
输入两个数到两个变量中,然后用赋值语句求它们的和,输出。但是,我们知道,在C++语言中任何数据类型都有一定的表示范围。而当两个被加数很大时,上述算法显然不能求出精确解,因此我们需要寻求另外一种方法。在读小学时,我们做加法都采用竖式方法,如图1。 这样,我们方便写出两个整数相加的算法。

在这里插入图片描述

如果我们用数组A、B分别存储加数和被加数,用数组C存储结果。则上例有A[1]=6,A[2]=5, A[3]=8,B[1]=5,B[2]=5,B[3]=2,C[4]=1,C[3]=1,C[2]=1,C[1]=1,两数相加如图2所示。

void add(int a[],int b[]){
	memset(c,0,sizeof(c));
	int len=a[0]>b[0]?a[0]:b[0];
	int g=0;
	for(int i=1;i<=len;i++){
		c[i]=a[i]+b[i]+g;
		g=c[i]/10;
		c[i]%=10;
	}
	if(g!=0)c[++len]=g;
	c[0]=len;
}

#include<iostream>
#include<string>
#include<cstring>
using namespace std;
int a[1000],b[1000],c[1000];
void read(int a[])
void write(int a[])void add(int a[],int b[])int main(){
	read(a);
	read(b);
	add(a,b);
	write(c);
}

高精度减法

bool check(int a[],int b[]){
	if(a[0]>b[0])return false;
	if(a[0]<b[0])return true;
	for(int i=a[0];i>=1;i--){
		if(a[i]<b[i])
		  return true;
	}
	return false;
}

void subtract(int a[],int b[]){
	if(check(a,b)){
		int *p;//交换两个数组
		p=a;
		a=b;
		b=p;
	}
	memset(c,0,sizeof(c));
	for(int i=1;i<=a[0];i++){
		if(a[i]<b[i]){
			a[i]+=10;
			a[i+1]--;
		}
		c[i]=a[i]-b[i];
	}
	//记录数组长度,末位清空
	int len=a[0];
	while(c[len]==0&&len!=1)len--;
	c[0]=len;
}

高精度乘以单精度

void multiply(int a[],int k){
	memset(c,0,sizeof(c));
	int g=0;
	for(int i=1;i<=a[0];i++){
		c[i]=a[i]*k+g;
		g=c[i]/10;
		c[i]%=10;
	}
	int len=a[0];
	while(g!=0){
		c[++len]=g%10;
		g/=10;
	}
	c[0]=len;
}

高精度乘以高精度

【算法分析】
  类似加法,可以用竖式求乘法。在做乘法运算时,同样也有进位,同时对每一位进行乘法运算时,必须进行错位相加,如图3、图4。
  分析c数组下标的变化规律,可以写出如下关系式:ci = c’i +c”i +…由此可见,c i跟a[i]*b[j]乘积有关,跟上次的进位有关,还跟原c i的值有关,分析下标规律,有c[i+j-1]= a[i]*b[j]+ x + c[i+j-1]; x=c[i+j-1]/10 ; c[i+j-1]%=10;

在这里插入图片描述

在这里插入图片描述
c[i+j-1]:=A[i]*b[j]

void multiply(int a[],int b[]){
	memset(c,0,sizeof(c));
	for(int i=1;i<=a[0];i++){
		for(int j=1;j<=b[0];j++){
			c[i+j-1]+=a[i]*b[j];
		}
	}
	int len=a[0]+b[0]-1;
	int g=0;
	for(int i=1;i<=len;i++){
		c[i]+=g;
		g=c[i]/10;
		c[i]%=10;
	}
	while(g!=0){
		c[++len]=g%10;
		g/=10;
	}
	c[0]=len;
}


高精度除单精度

做除法时,每一次上商的值都在0~9,每次求得的余数连接以后的若干位得到新的被除数,继续做除法。因此,在做高精度除法时,要涉及到乘法运算和减法运算,还有移位处理。当然,为了程序简洁,可以避免高精度除法,用0~9次循环减法取代得到商的值。这里,我们讨论一下高精度数除以单精度数的结果,采取的方法是按位相除法。

void divide(int a[],int k){
	memset(c,0,sizeof(c));
	int r=0;
	for(int i=a[0];i>=1;i--){
		c[i]=a[i]+r*10;
		r=c[i]%k;
		c[i]/=k;
	}
	int len=a[0];
	while(c[len]==0&&len!=1)len--;
	c[0]=len;
}

实质上,在做两个高精度数运算时候,存储高精度数的数组元素可以不仅仅只保留一个数字,而采取保留多位数(例如一个整型或长整型数据等),这样,在做运算(特别是乘法运算)时,可以减少很多操作次数。例如图5就是采用4位保存的除法运算,其他运算也类似。具体程序可以修改上述例题予以解决,程序请读者完成。

在这里插入图片描述

高精度除高精度

高精除以低精是对被除数的每一位(这里的“一位”包含前面的余数,以下都是如此)都除以除数,而高精除以高精则是用减法模拟除法,对被除数的每一位都减去除数,一直减到当前位置的数字(包含前面的余数)小于除数(由于每一位的数字小于10,所以对于每一位最多进行10次计算)

void divide(int a[],int b[]){
	memset(c,0,sizeof(c));
	while(!check(a,b)){
		subtract(a,b);
		add(c,1);
	}
} 

void subtract(int a[],int b[]){
	for(int i=1;i<=a[0];i++){
		if(a[i]<b[i]){
			a[i]+=10;
			a[i+1]--;
		}
		a[i]-=b[i];
	}
	int len=a[0];
	while(a[len]==0&&len!=1)len--;
	a[0]=len;
}

void add(int a[],int k){
	a[1]+=k;
	int len=1;
	while(a[len]>9){
		a[len+1]+=a[len]/10;
		a[len]%=10;
		len++;
	}
	if(len>a[0])a[0]=len;
}

bool check(int a[],int b[]){
	if(a[0]>b[0])return false;
	if(a[0]<b[0])return true;
	for(int i=a[0];i>=1;i--){
	    if(a[i]<b[i])return true;
	}
	return false;
}

完整代码

#include<iostream>
#include<cstring>
using namespace std;
int a[101],b[101],c[101],d,i;   
void init(int a[]) 
{    string s; 
	cin>>s;                        //读入字符串s 
	a[0]=s.length();           //用a[0]计算字符串 s的位数 
	for(i=1;i<=a[0];i++)
	a[i]=s[a[0]-i]-'0';          //将数串s转换为数组a,并倒序存储. 
}
void print(int a[])              //打印输出
{
	if (a[0]==0){cout<<0<<endl;return;}
	for(int i=a[0];i>0;i--) cout<<a[i];
	cout<<endl;
	return ;
}
int compare (int a[],int b[])  
			//比较a和b的大小关系,若a>b则为1,a<b则为-1,a=b则为0 
{    int i;
	if(a[0]>b[0]) return 1;                    //a的位数大于b则a比b大 
	if(a[0]<b[0]) return -1;                   //a的位数小于b则a比b小 
	for(i=a[0];i>0;i--)                           //从高位到低位比较 
	{
		if (a[i]>b[i]) return 1; 
		if (a[i]<b[i]) return -1;
	} 
	return 0;                                        //各位都相等则两数相等。 
} 

void numcpy(int p[],int q[],int det)      //复制p数组到q数组从det开始的地方
{
	for (int i=1;i<=p[0];i++) q[i+det-1]=p[i];
	q[0]=p[0]+det-1;
}
void jian(int a[],int b[])               //计算a=a-b
{ 
	int flag,i; 
	flag=compare(a,b);              //调用比较函数判断大小 
	if (flag==0) {a[0]=0;return;}   //相等 
	if(flag==1)                             //大于   
	{
		for(i=1;i<=a[0];i++) 
		{
			if(a[i]<b[i]){ a[i+1]--;a[i]+=10;}         //若不够减则向上借一位 
			a[i]-=b[i];
		} 
		while(a[0]>0&&a[a[0]]==0) a[0]--;               //修正a的位数 
		return;
	} 
} 
void chugao(int a[],int b[],int c[])
{
	int tmp[101]; 
	c[0]=a[0]-b[0]+1;
	for (int i=c[0];i>0;i--)
	{
		memset(tmp,0,sizeof(tmp));                              //数组清零 
		numcpy(b,tmp,i);
		while(compare(a,tmp)>=0){c[i]++;jian(a,tmp);}  //用减法来模拟
	}
	while(c[0]>0&&c[c[0]]==0)c[0]--;
	return ;
}

int main()
{
  	memset(a,0,sizeof(a));
  	memset(b,0,sizeof(b));
  	memset(c,0,sizeof(c));
  	init(a);init(b);
  	chugao(a,b,c);
  	print(c);
  	print(a);
  	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值