高精度算法

1.1 描述

高精度算法,属于处理大数字的数学计算方法。在一般的科学计算中,会经常算到小数点后几百位或者更多,当然也可能是几千亿几百亿的大数字。一般这类数字我们统称为高精度数,高精度算法是用计算机对于超大数据的一种模拟加,减,乘,除,乘方,阶乘,开方等运算。对于非常庞大的数字无法在计算机中正常存储,于是,将这个数字拆开,拆成一位一位的,或者是四位四位的存储到一个数组中, 用一个数组去表示一个数字,这样这个数字就被称为是高精度数。高精度算法就是能处理高精度数各种运算的算法,但又因其特殊性,故从普通数的算法中分离,自成一家。

对于这类问题,不要指望int double这些东西了,基本数据类型不可能存的下。我们可以把这两个数当成字符串输入到数组中,然后模拟手动的竖式运算(不会的话,回去上小学)得出结果。

说白了,高精度计算就是解决long long也解决不了的问题。


1.2 分析与处理数据

1.2.1 当要处理的数据很长时,我们可以采用字符串的方式输入,这样可以输入很长的数,利用字符串函数和操作运算,将每一位数取出,存入一个数组里。

//1.2.1 数据的接收和存储方法 
void input()
{
	string s;
	cin>>s;//读入字符串
	a[0]=s.length();//取s字符串的长度,并存入a[0]中
	for(i=1;i<=a[0];i++)
	{
		a[i]=s[a[0]-i]-'0';//将字符串s转化为数字存入a数组中,并倒叙存储 
		//为什么要倒叙?为了给进位留足空间
		//高精度的存储是把每一位单独存储,且是倒序存储,数组a[1]是这个数的个位,a[2]是这个数的十位,以此类推
	}
}

1.2.2 接收时使用字符串,所以它的位数等于字符串的长度

1.2.3 当两个操作数长度不一样时,需要进行补零

//1.2.3 补零问题
void NumFill()
{
	string s1,s2;
	int len1,len2,i;
	cin>>s1>>s2;//读入字符串
	len1=s1.length();//求字符串1长度 
	len2=s2.length();//求字符串2长度
	if(len1<len2)//如果字符串1长度小于字符串2长度 
	{//对字符串1补零 
		for(i=1;i<=len2-len1;i++)//len2-len1就是需要补零的长度 
		{
			s1='0'+s1;
		}
	}
	else//否则字符串1长度大于字符串2长度 
	{//对字符串2补零 
		for(i=1;i<=len1-len2;i++)//len1-len2就是需要补零的长度 
		{
			s2='0'+s2;
		}
	}
} 

1.2.4 进位与借位

//1.2.4 进位处理
void plus()//加法进位 
{
	c[i]=a[i]+b[i];//伪代码,c[i]来存储高精度数,a[i]和b[i]是被分离的2个加数。
	if(c[i]>=10)//如果a[i]+b[i]大于0了,很显然,要进位。不知道的给我重读1年级数学
	{
		c[i]=c[i]%10;//把得到的结果分离出来
		++c[i+1];//那么后一位进一 
	} 
}
void minus()//减法借位 
{
	if(a[i]<b[i])//先判断被减数是否小于减数 
	{
		--a[i+1];//后一位借位,所以减1 
		a[i]+=10;//那么当前加10 
	}
	c[i]=a[i]-b[i];//这个时候算差 
}
void multiplication()//乘法进位 
{
	c[i+j-1] = a[i]*b[i]+x+c[i+j-1];//x表示的是进位数
	//我们再声明一个数组c来储存答案。大家通过一个简单的乘法运算进行模拟就可以看出。 
	//结果 = 当前乘积a[i]*b[i] + 进位数x + 原数 
	x=c[i+j-1]/10;//需要的进位数 
	c[i+j-1]%=10;//取下一位数
	//这里应该有for循环的 
}

1.3 高精度计算算法和思想

1.3.1 高精度加法

在读小学时,我们做加法都采用竖式方法,利用竖式,我们可以写出两个整数相加的高精度算法。

那么,要想求出高精度数据,就需要一定的步骤算法。

1.字符转数字,且低位对齐;

2.a、b两个数组的长度,取较长的。相加时,考虑进位;

3.考虑得数的长度和0位;

4.逆序输出。

Ps:其实低位对齐和逆序,只是方便计算和顺应算法思想罢了,不必要去思考为什么要这样做。

//1.3.1 高精度加法 
#include <cstdio>
#include <iostream>
#include <cstring>
#define r(i,a,b) for(i=a;i<=b;i++)//为了省事而已 
using namespace std;
int main()
{
	char a1[101]={},b1[101]={}; 
	int a[101]={},b[101]={},c[101]={},lena,lenb,lenc,i,x;
	gets(a1);
	gets(b1);
	lena=strlen(a1);//取长度 
	lenb=strlen(b1);//取长度 
	r(i,0,lena-1)//从0到数组长度-1正好把数据读完 
	{
		a[lena-i]=a1[i]-48;//字符转数字 
	}
	r(i,0,lenb-1)//从0到数组长度-1正好把数据读完 
	{
		b[lenb-i]=b1[i]-48;//字符转数字 
	}
	lenc=1;//用来处理数组长度 
	x=0;//用来计算进位 
	while(lenc<=lena || lenc<=lenb)//开始计算,算法核心 
	{
		c[lenc]=a[lenc]+b[lenc]+x;//得到的结果,应该是a[]+b[],当然还要加上进位 
		x=c[lenc]/10;//其实不用写if,如果此答案低于10得到的进位还是0,超过10就是1 
		c[lenc]%=10;//此答案取后一位,如果只有1位数,还是本身 
		lenc++;//继续判断下一位 
	}
	if(x==0)//如果最后一位可以进位 
		lenc--;
	for(i=lenc;i>=1;i--)//逆序输出 
		cout<<c[i];
	cout<<endl;
	return 0;
}

1.3.2 高精度减法

类似加法,可以用竖式求减法。在做减法运算时,需要注意的是:被减数必须比减数大,同时需要处理错位以及前导0。

步骤和算法设计

1.字符转数字,且低位对齐;

2.取a,b2个数组长度更长的那个,注意可能会有负号;

3.计算时可能会有进位;

4.高位是否存在;

5.逆序输出。

//1.3.2 高精度减法 
#include <cstdio>
#include <cstring>
#include <iostream>
#define r(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int a[257],b[257],c[257],lena,lenb,lenc,i;
char n[257],n1[257],n2[257];
int main()
{
	cin>>n1>>n2;
	if(strlen(n1)<strlen(n2) || (strlen(n1)==strlen(n2) && strcmp(n1,n2)<0))//如果被减数小于减数,值为负,两者交换
	{
		strcpy(n,n1);
		strcpy(n1,n2);
		strcpy(n2,n);//字符交换 
		cout<<'-';//先输出负号 
	}
	lena=strlen(n1);
	lenb=strlen(n2);
	r(i,0,lena-1)//被减数存入数组a
	{
		a[lena-i]=int(n1[i]-'0');//字符转数字 
	}
	r(i,0,lenb-1)//减数存入数组a
	{
		b[lenb-i]=int(n2[i]-'0');//字符转数字 
	}
	i=1;//控制当前数组下标
	while(i<=lena || i<=lenb)//开始计算,核心算法 
	{
		if(a[i]<b[i])//如果被减数比减数小,要借位 
		{
			a[i]=a[i]+10;
			a[i+1]--;
		}
		c[i]=a[i]-b[i];//因为判断是否借位了,直接减即可 
		i++;
	}
	lenc=i;
	while((c[lenc]==0)&&(lenc>1))//判断高位是否为0,最高位0不输出 
		lenc--;
	for(i=lenc;i>=1;i--)//逆序输出 
		cout<<c[i];
	cout<<endl;
	return 0;
}

1.3.3 高精度乘法

类似加法,用竖式求乘法。在做乘法运算时,同样有进位,同时对每一位进行乘法运算时,必须进行错位相加。

//1.3.3 高精度乘法 
#include <cstdio>
#include <cstring>
#include <iostream>
#define r(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int a[257],b[257],c[257],lena,lenb,lenc,i,j,x;
char a1[257],b1[257];
int main()
{
	cin>>a1>>b1;
	lena=strlen(a1);
	lenb=strlen(b1);
	r(i,0,lena-1)
	{
		a[lena-i]=int(a1[i]-'0');
	}
	r(i,0,lenb-1)
	{
		b[lenb-i]=int(b1[i]-'0');
	}
	r(i,1,lena)
	{
		x=0;//存放进位
		r(j,1,lenb)//对乘数的每一位都处理 
		{
			c[i+j-1]=a[i]*b[j]+x+c[i+j-1];//结果 = 当前乘积a[i]*b[i] + 进位数x + 原数 
			x=c[i+j-1]/10;
			c[i+j-1]%=10;
		}
		c[i+lenb]=x;//是否需要进位 
	}
	lenc=lena+lenb;//根据乘法的原理计算计算最大长度
	//99*99,最大长度是4
	//11*10,最大长度是3 
	while(c[lenc]==0 && lenc>1)//删除前导0 
	{
		lenc--;
	}
	for(i=lenc;i>=1;i--)
	cout<<c[i];
	cout<<endl;
	return 0;
}

1.3.4 高精度除法,高精除以低精

做除法时,每一次的商的值都在0~9,每次求得的余数连接以后的若干位得到新的被除数,继续做除法。因此在做高精度除法时,要涉及到乘法运算和减法运算以及移位处理。

为程序简洁,可以避免高精度乘法,用0~9次循环减法取代得到商的值,对于高精度数除以低精度数,我们采用按位相除法。

//1.3.4 高精度除法,高精除以低精 
#include <cstdio>
#include <cstring>
#include <iostream>
#define r(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int b,a[257],c[257],lena,lenc,i,j,x=0;//x是余数 
char a1[257],c1[257];
int main()
{
	cin>>a1>>b;//注意,此时高精除以低精 
	lena=strlen(a1); 
	r(i,0,lena-1)
	{
		a[i+1]=a1[i]-'0';
	}
	r(i,1,lena)//模拟除法竖式,按位相除 
	{
		c[i]=(x*10+a[i])/b;
		x=(x*10+a[i])%b;
	}
	lenc=1;
	while(c[lenc]==0&&lenc<lena)//删除前导0 
	lenc++;
	for(i=lenc;i<=lena;i++)
	cout<<c[i];
	cout<<endl;
	return 0;
}

本来还有个1.3.5的,高精数除以高精数,A.pro智力有限还没想好..

猜你喜欢

转载自blog.csdn.net/apro1066/article/details/80710503