大整数操作——四则运算、进制转换、比较大小

对于一道A+B的题目,如果A和B在int型范围内,这个程序会很容易写出,但是A和B是几百位甚至几千位的整数呢?不管是int型还是长整型都没有办法进行运算。这时就只能模拟四则运算的过程。在本篇博客中,博主主要就自己的认识给大家分享一下关于大整数的操作,可能会因为能力问题而解释不到位,希望读者多多包涵。


大整数的存储

先来看一看大整数的存储。为了方便获取大整数的长度,一般会定义一个结构体,用于存放大整数及它的长度,此外还需要初始化结构体,即用一个构造函数。在这里我们用一个int型数组来存放大整数:d[1000],即一个1000位以内的大整数。

struct bign{
    
    
	int d[1000];
	int len;
	bign(){
    
    
		memset(d,0,sizeof(d));
		len=0;
	}
};

在输入大整数时,一般都是用字符串读入的吧,但是结构体里的数组是int型的,因此我们不妨将该字符串转换成整型然后存入bign结构体中。本篇博客中规定整数的高位存储在数组的高位,整数的低位存储在数组的地位。 由于输入str时,整数的高位会变成数组的地位,而整数的低位会变成数组的高位,因此为了让整数在bign中是顺序位,需要让字符串倒着存入数组中。

bign change(string str){
    
    
	bign a;
	a.len=str.length();
	for(int i=0;i<a.len;i++)
		a.d[i]=str[a.len-1-i]-'0';
	return a;
}

再来看看如何比较两个大整数的大小:我们可以先判断他们的长度,长度长的那一个数肯定大,长度小的那一个数肯定小。如果相等,则从数组低位进行比较(数组低位即大整数的高位),直至1出现一个不等的位。对于两个大整数a,b,我们规定如果a大则返回1,a小则返回-1,相等则返回0,于是有:

int compare(bign a,bign b){
    
    
	if(a.len>b.len) return 1;	//a大
	else if(b.len>a.len) return -1;	//b大
	else{
    
    
		for(int i=a.len-1;i>=0;i--){
    
    
			if(a.d[i]>b.d[i]) return 1;	//a大
			else if(b.d[i]>a.d[i]) return -1;	//b大
		}
		return 0;	//a和b相等
	}
}

做好以上这些准备后,下面可以正式切入主题了,先来看一看大整数的四则运算吧。


大整数的四则运算

1、高精度加法

以456+78为例子,先令进位carry为0。
1、8+6+carry=14,取个位数4作为该位的结果,取十位数1作为进位。
2、5+7+carry=13,取个位数3作为该位的结果,取十位数1作为进位。
3、4+0+carry=5,取个位数5作为该位的结果,不进位,因为carry=0。

因此答案为534。此时可以把以上步骤归纳为:对其中一位进行加法时,将该位上的两个数字相加,相加后的个位数作为该位的结果,十位数作为进位,由此可以得出高精度加法运算的代码:

bign add(bign a,bign b){
    
    
	bign c;
	int carry=0;	//进位
	for(int i=0;i<a.len||i<b.len;i++){
    
    
		int temp=a.d[i]+b.d[i]+carry;
		c.d[c.len++]=temp%10;	//取个位作为该位的结果
		carry=temp/=10;	//取十位作为新的进位
	}
	if(carry!=0)	//最后可能存在进位
		c.d[c.len++]=carry;
	return c;
}

需要注意的是,该代码有一个前提条件,即a和b都是正整数。如果有一个是负数,可以采用高精度的减法;如果两个都是负数,则先取他们的绝对值进行运算,最后在将运算结果加上负号即可。

2、高精度的减法

还是以456-78为例。
1、6-8<0,此时不够减,需要向高位借位,即5减1等于4,该位结果为16-8=8。
2、4-7<0,此时不够减,需要向高位借位,即4减1等于3,该位结果为14-7=7。
3、3-0=3。

因此答案为378。现在对于以上步骤我们进行归纳:对某一步,比较被减位和减位,看他们够不够减,如果不够减,则向高位借位,高位减1,被减位加10,此时再进行计算;如果够减,则直接减去。将减去的结果作为该位的结果。最后一步还要注意的是,高位可能有多余的0,要去除他们,但是也要保证结果至少有一位数。由此可以得出减法运算的代码:

bign sub(bign a,bign b){
    
    
	bign c;
	for(int i=0;i<a.len||i<b.len;i++){
    
    
		if(a.d[i]<b.d[i]){
    
    	//不够减则向高位借位
			a.d[i+1]--;
			a.d[i]+=10;
		}
		c.d[c.len++]=a.d[i]-b.d[i];
	}
	while(c.len-1>=1&&c.d[c.len-1]==0)	//消除高位多余的0
		c.len--;
	return c;
}

需要注意的数,对于a和b来说,被减数一定要大于减数,因此在调用该函数时,先比较a和b的大小,若a小则进行交换,并且输出负号,再使用该函数。

3、高精度与低精度的乘法

高精度与低精度的乘法就是bign型与int型的乘法。
以147×35为例,先定义一个进位carry并令其为0。
1、7×35=245,245+carry=245,取个位数5作为该位的结果,高位部分24作为进位。
2、4×35=140,140+carry=164,取个位数4作为该位的结果,高位部分16作为进位。
3、1×35=35,35+carry=51,取个位数1作为该位的结果,高位部分5作为进位。
4、乘完了,如果进位不为0,就把进位直接作为结果的高位。

因此答案为5145,对每一步进行归纳:取bign的某位与int型整数进行相乘,再与进位相加,所得结果的个位作为该位的结果,高位部分作为新的进位。所以有:

bign multi(bign a,int b){
    
    
	bign c;
	int carry=0;	//定义进位
	for(int i=0;i<a.len;i++){
    
    
		int temp=a.d[i]*b+carry;
		c.d[len++]=temp%10;	//取个位作为结果
		carry=temp/10;	//取高位作为新的进位
	}
	while(carry!=0){
    
    	//乘法的进位可能不止一位
		c.d[len++]=carry%10;
		carry/=10;
	}
	return c;
}

如果a和b中存在一个负数,则先记录负号,再取绝对值相乘。如果两个都是负数,则取他们的绝对值进行相乘。

5、高精度与低精度的除法

以1234/7为例,先定义一个余数r为0。
1、(1+r*10)与7进行比较,不够除,因此该位商为0,余数为1。
2、(2+r*10)与7进行比较,够除,因此该位商为1,余数为5。
3、(3+r*10)与7进行比较,够除,因此该位商为7,余数为4。
4、(4+r*10)与7进行比较,够除,因此该位商为6,余数为2。
对上述步骤进行归纳:上一步的余数乘以10(十进制)加上该步的位,得到的结果与除数进行比较,如果不够除,该位商为0;如果够除,则商即为对应的商,余数为对应的余数。最后一步还要去除高位多余的0,并且保证结果至少有一位数。于是有:

bign divide(bign a,int b,int &r){
    
    
	bign c;
	c.len=a.len;	//先令其长度相等
	r=0;	//令余数r为0
	for(int i=a.len-1;i>=0;i++){
    
    	//从高位开始除
		r=r*10+a.d[i];	//和上一步遗留的余数组合
		if(r<b) c.[i]=0;	//不够除
		else{
    
    	//够除
			c.d[i]=r/b;	//商作为该位的结果
			r%=b;	//新的余数
		}
	}
	while(c.len-1>=1&&c.d[c.len-1]==0)	//去除高位的0
		c.len--;
	return c;
}

大整数的进制转换

将大整数a的m进制转换成n进制,我们可以先调用稍加改动的divide()函数,把单次计算取余的结果存入string数组的ans中。

获取余数:

bign SysConvert(bign a,int m,int n,int &r){
    
    
	bign c;
	c.len=a.len;
	r=0;
	for(int i=a.len-1;i>=0;i--){
    
    
		r=r*m+a.d[i];	//注意此时是将余数乘m,因为是m进制
		if(r<n)	c.d[i]=0;
		else{
    
    
			c.d[i]=r/n;
			r%=n;	
		}
	}
	while(c.len-1>=1&&c.d[c.len-1]==0)
		c.len--;
	return c;
}

存储余数:

	string ans;
	int r;
	bign a=change(str);
	do{
    
    
		a=SysConvert(a,m,n,r);
		ans.push_back(r+'0');
	}while(!(a.d[0]==0&&a.len==1));

此时ans中的逆序就是n进制的a。

猜你喜欢

转载自blog.csdn.net/qq_44888152/article/details/106969793