大整数高精度(包括四则运算,逻辑运算)

先上代码
if(只想用)转到代码之后,有怎么声明跟四则运算的使用方式
else 在使用方式后面关于高精的算法,对每一个部分(加、减、乘、除跟逻辑运算)都有介绍

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
struct big{
	char num[1000];
	big(){
		memset(num,0,sizeof(num));
	}
	big value(const char *a){
		memset(num,0,sizeof(num));
		int m=strlen(a);
		for(int i=0;i<m;i++)num[i]=a[m-i-1];
		return *this;
	}
};
bool isle(big a,big b,int p=0){
	int m=strlen(a.num),n=strlen(b.num)+p;
	if(m!=n)return m<n;
	for(int i=m;i>=p;i--)
		if(a.num[i]!=b.num[i-p])return a.num[i]<b.num[i-p];
    return false;
}
bool isbg(big a,big b){return isle(b,a);}
bool isleo(big a,big b){return !isbg(a,b);}
bool isbgo(big a,big b){return !isle(a,b);}
bool issame(big a,big b){return !isle(a,b)&&!isbg(a,b);}
bool isdef(big a,big b){return !issame(a,b);}
big add(big a,big b,int p=0){
	big c;int m=strlen(a.num),n=strlen(b.num)+p,mymax=max(m,n);
	for(int i=0;i<p;i++) c.num[i]=a.num[i];
	int i=0,temp=0,g=0;
	for(i=p;i<mymax;i++){
	temp=i<m&&i<n?a.num[i]+b.num[i-p]-2*'0'+g: a.num[i]+b.num[i-p]-'0'+g;
	c.num[i]=temp%10+'0';
	g=temp>=10?1:0;
	}
	c.num[i]=g?'1':0;
	return c; 
}
big gew(big a,int b){
	big c;int m=strlen(a.num),temp=0,g=0,i=0;
	for(i=0;i<m;i++){
		temp=(a.num[i]-'0')*b+g;
		c.num[i]=temp%10+'0';
		g=temp/10;
	}
	c.num[i]=g?g+'0':0;
	return c;
}
big marix(big a,big b){
	big c;int m=strlen(b.num);
	for(int i=0;i<m;i++)c=add(c,gew(a,b.num[i]-'0'),i);
	return c;
}
void show(big a){
	int m=strlen(a.num);
	while(m)cout<<a.num[--m];
}
big cut(big a,big b,int p=0){
	big c;int m=strlen(a.num),n=strlen(b.num)+p;
	if(isle(a,b,p)) fprintf(stderr,"Fail");
	for(int i=0;i<p;i++) c.num[i]=a.num[i];
	int i=0,temp=0,g=0;
	for(i=p;i<m;i++){
		temp=i<n?a.num[i]-b.num[i-p]-g: a.num[i]-'0'-g;
		if(temp<0){temp+=10;g=1;}else g=0;
		c.num[i]=temp+'0';
	}	
		for(int o=i-1;o>=0;o--)
		if(c.num[o]!='0')break;
		else c.num[o]=0;
	return c;
}
big sub(big a,big b){
	big c;int m=strlen(a.num),n=strlen(b.num),p=m-n;
	if(isle(a,b))return c.value("0");
	int temp=0,g=0;
  for(int i=p;i>=0;i--){
  	if(!isle(a,b,i)){int o=1;
  		for(o=1;;o++)
		  {if(isle(a,gew(b,o),i))break;}
  		c.num[i]=o+'0'-1;a=cut(a,gew(b,o-1),i);
	  }
	  else c.num[i]='0';
  }
  for(int m=strlen(c.num)-1;m>=0;m--)
  if(c.num[m]!='0')break;else c.num[m]=0;
  return c;
}

声明与赋值:
声明 :big a;//声明一个高精度数a并且默认赋值为0
赋值 :a.value(const char *);//用字符串对a赋值
逻辑运算:
bool isle(big a,big b,int p=0):小于比较
逻辑运算其中唯一一个有第三个参数表达位置的,第三个参数表示从a的第p个位置开始比较a与b,并返回其实a小于b的值
例如,当p=0时,则与一般的比较无异,而当p为1时,就从a的十位位置开始比较。例如456与78,在p是0时,比较后返回假,但当p是1
是,则返回真(从十位开始也即比较45跟78);
bool isbg(big a,big b):返回a大于b的值;
bool isleo(big a,big b) :返回a小于或等于b
bool isbgo(big a,big b): 返回a大于或等于b
bool issame(big a,big b):返回a与b相等
bool isdef (big a,big b):返回a与b不等
四则运算:
big add(big a,big b,int p=0):返回a与b的和
最后一个参数表示从a的第p位开始加,至于第p位是什么意思,参照前面小于比较的p,只是普通加的话就不用去理会了
例如,要求a+b的值,直接add(a,b)就可以了
big cut(big a,big b,int p=0):返回a与b的差(前提是a大于b)
big marix(big a,big b):返回a与b的积
big sub(返回a除以b的商);
显示操作:
void show(big a):显示一个高精值,当然也可以显示四则运算的结果
把一个高精数传进去就可以了,如show(a)或show(add(a,b));
————————————————————————————
大家看完模板,要看后面的部分的话,后面需要大家去理解跟调试的时间可能会比较多,我力争讲透,但能力有限,所有很多要靠大家自行理解。也希望大家看完都能独立编码;
——————————————————————————————
大致的操作就是前面几项了,有兴趣了解幂跟幂模的也可以继续看下去,幂跟模以及幂模的模板放在最后讲;
后面就是算法(干货)部分了:
有些数字,无法使用内置运算(连long long都无法表示),这个时候,大整数(或称作高精度数)就派上用场了,高精度也支持各种与内置类型(逻辑运算跟四则运算)
至于实现嘛……,就看技术了 ,就看下面的算法分析了;
首先考虑储存方式,内置类型已经枪毙,那用整数数组储存的话,又没办法确定大小(即使使用动态数组,能确定大小空间又会占用到太大,速度也很抓急,当然也有用动态数组加字符串的,但是实现起来较为复杂,而且时间复杂度也不是很低);这个pass掉之后,剩下的方式其实也不多;最好的选择就是,就是,就是人脑储存这个高级储存方式了,嗯,人脑储存,那运算也靠人脑这个高级中枢计算就可以了大整数算法部分讲解到此结束 o_o
比较好的方式是利用字符串(字符数组)来储存各位的数字,这样子处理,一方面只要字符数组开得够大,就可以保存所需的大整数,另一方面,直接利用字符串,可以直接由标准输入中读取一个大整数或是只用一次转换就可以由标准输入读取一个大整数;相对于其他方式,字符串更好处理,编码复杂度跟时间复杂度都可以大大地降下来;
储存方式考虑完,接下来是储存的形式,直接正序储存还是要逆序储存;由于我们运算的习惯是由个位向高位,数组下标由低位向高位去看。因此逆序储存对于我们的运算实现起着十分重要的作用,在后面的运算实现中,大家可以看到运算实现与此处的联系;
那么,大整数的雏形基本就出来了,同时为了更具普适性,将其用结构体封装,而不直接用字符串来做,定义跟赋值的代码如下:

struct big{
	char num[1000];
	big(){
		memset(num,0,sizeof(num));
	}
	big value(const char *a){
		memset(num,0,sizeof(num));//记得先清空再处理
		int m=strlen(a);
		for(int i=0;i<m;i++)num[i]=a[m-i-1];//逆序储存
		return *this;
	}
};

同时给出显示的函数,由于这个过于简单,就不解释了

void show(big a){
	int m=strlen(a.num);
	while(m)cout<<a.num[--m];//逆序改成正序输出
}

接下来讲一下逻辑运算(包括大小判断跟不等判断)
先讲一下小于的实现,基本思路不难想:先判断两个数的位数,位数不同,比较位数,否则,由最高位开始比较就可以了;
当引入另一个参数p表示从a的第p位开始比较时,做一些调整就可以了:位数比较时相当于b增大了10的p次倍,把b的位数加上p的值就可以了,接下来的比较跟默认的比较就基本相同了;
下面给代码:

bool isle(big a,big b,int p=0){
	int m=strlen(a.num),n=strlen(b.num)+p;//将b的位数“增大”p位;
	if(m!=n)return m<n;//预判比较
	for(int i=m;i>=p;i--)
		if(a.num[i]!=b.num[i-p])return a.num[i]<b.num[i-p];
		//此处由高处向低处比较记得要考虑到之前对b的位数的操作
    return false;
}

剩下的逻辑判断呢,假如还是一个一个写的话那就真的是图样图森破啊,运用逻辑方面的知识推导一下其他的不难推出,例如,大于的实现就可以为小于倒过来就可以了
也即是a>b可以由b<a得到;
同理a>=b可以由a<b取其补集就可以了(即a>=b可写作!a<b);
其余相信大家这样子聪明应该都是可以自己推出来的,此处直接给代码

bool isbg(big a,big b){return isle(b,a);}//大于判断
bool isleo(big a,big b){return !isbg(a,b);}//小于等于
bool isbgo(big a,big b){return !isle(a,b);}//大于等于
bool issame(big a,big b){return !isle(a,b)&&!isbg(a,b);}//等于判断
bool isdef(big a,big b){return !issame(a,b);}//不等判断

接下来实现加法,考虑到乘法需要从不同的位置进行(如第一次直接从个位进行的加法,第二次就得从十位开始了),此处加入一个参数p表示由a的第p个位置开始加,默认p为0,即平常的加法,对于位置的处理跟上述的小于判断是相同的,此处就不再讲了
加法只需要考虑进位处理,其他的都不会很难,因此此处也不细讲,直接给出代码,大家若想要学透可以细细品味,真正将其掌握下来,多留意一下实现的细节,下面直接上代码:

big add(big a,big b,int p=0){
	big c;int m=strlen(a.num),n=strlen(b.num)+p,mymax=max(m,n);
	for(int i=0;i<p;i++) c.num[i]=a.num[i];
	int i=0,temp=0,g=0;//进位联系g,temp表示竖式加法加下来得到的各位的值
	for(i=p;i<mymax;i++){
	temp=i<m&&i<n?a.num[i]+b.num[i-p]-2*'0'+g: a.num[i]+b.num[i-p]-'0'+g;
	c.num[i]=temp%10+'0';
	g=temp>=10?1:0;//各位加起来大于或等于10的时候要进位
	}
	c.num[i]=g?'1':0;//可能最后会有进位,此处通过判断最后g的值即可处理
	return c; 
}

减法的实现思路跟加法基本相同,不同的是,当a比b要小时,向标准错误输出中输出减法失败的标志,不进行减法,这个是因为要配合之后的除法的,我们在做减法的时候保证传进来的一定是a不小于b
同样,细节需要大家去看,此处不一一列举出来,注释在比较关键的地方,其他也有很多很重要的地方,靠大家慢慢体会了
代码也一并给出:

big cut(big a,big b,int p=0){
	big c;int m=strlen(a.num),n=strlen(b.num)+p;
	if(isle(a,b,p)) {fprintf(stderr,"Fail");return c;}//减法失败
	for(int i=0;i<p;i++) c.num[i]=a.num[i];
	int i=0,temp=0,g=0;//g来进行各位间的联系,temp表示各位相减的结果
	for(i=p;i<m;i++){
		temp=i<n?a.num[i]-b.num[i-p]-g: a.num[i]-'0'-g;
		if(temp<0){temp+=10;g=1;}else g=0;//不足减去时要“借位”,同时g要标记
		c.num[i]=temp+'0';
	}	
		for(int o=i-1;o>=0;o--)
		if(c.num[o]!='0')break;
		else c.num[o]=0;
	return c;
}

讲完加法跟减法,我们来看一下乘跟除
做乘法时,我们不妨先将问题分治一下,变成高精度a跟一个个位数相乘,之后再把这一些结果都加起来,其中第一次由个位开始,第二次由十位开始,之后的以此类推下去(此处就体现了那个参数p的巨大作用了);
同样,细节不细讲,靠大家体会,主要也是作者懒 ,下面给出实现:
高精与一个个位乘法:

big gew(big a,int b){
	big c;int m=strlen(a.num),temp=0,g=0,i=0;
	for(i=0;i<m;i++){
		temp=(a.num[i]-'0')*b+g;
		c.num[i]=temp%10+'0';
		g=temp/10;
	}
	c.num[i]=g?g+'0':0;
	return c;
}

有趣的是,只要高精a的每一位跟b相乘都不会超过三位数的话,这个函数是可以计算到a乘以两位数的,这个大家有时间不妨也试一下
至于为什么叫做gew,表示乘法中ge(个)位是后边广泛(w)的算法的前提
依靠这个gew,乘法就很简单了:

big marix(big a,big b){
	big c;int m=strlen(b.num);
	for(int i=0;i<m;i++)c=add(c,gew(a,b.num[i]-'0'),i);
	return c;
}

除法也相类似,只需要从最高位开始,先算出两者的位数差,然后借助功能强大的参数p(减数时跟小于时表明从a的第几位算起的辣个p),再模拟竖式进行除法运算就可以了;
直接给代码:

big sub(big a,big b){
	big c;int m=strlen(a.num),n=strlen(b.num),p=m-n;
	if(isle(a,b))return c.value("0");
	int temp=0,g=0;
  for(int i=p;i>=0;i--){
  	if(!isle(a,b,i)){int o=1;
  		for(o=1;;o++)
		  {if(isle(a,gew(b,o),i))break;}
  		c.num[i]=o+'0'-1;a=cut(a,gew(b,o-1),i);
	  }
	  else c.num[i]='0';
  }
  for(int m=strlen(c.num)-1;m>=0;m--)
  if(c.num[m]!='0')break;else c.num[m]=0;
  return c;
}

关于检验的,可以通过结果与内置运算结果比较,查看结果是否正确就可以了;例如要检验除法,就可以直接写出以下main()中的检验的代码:

int main(){
char a[100[={0),b[100]={0};
big m,n;
while(cin>>a>>b){
m.value(a),n.value(b);
long long q,w;
sscanf(a,"%lli",&q),sscanf(b,"%lli",&w);
show(sub(m,n));cout<<"  "<<q/w<<endl;
}
return 0;
}

模跟幂以及幂模放在后续再来讲,刚学高精度的同学可以慢慢消化一下前面的内容,以及里面的细节——很多编程的细节其实靠自己去发现反而会有趣的多,而且学到的也会更多,再加上里面很多东西我也很难讲得清,靠大家自己去理解会更好一些,因此前面基本都是要大家去自己理解的。基本的运算到此结束,后面补上幂,幂模以及模
当然了,与之前一样,不解释太多,只给代码,大家自己理解(可以参阅快速幂跟快速幂模)

big e,mo;
big mypow(big a,int b){      //求幂
 if(b==1)return e=a;
    if(b>1){
     mypow(a,b>>1);
     if(b & 01)return e=marix(marix(e,e),a);
     else return e=marix(e,e);
 } 
}
big mode(big a,big b){     //求模
 big c;
 c=cut(a,marix(b,sub(a,b)));
 if(strlen(c.num)>0) return c;
 else {c.value("0");return c;}
}
big qumode(big a,int c,big b){   //快速模
 if(c==1) return mo=mode(a,b);
 if(c>1){
      qumode(a,c>>1,b);
   if(c & 01)return mo=mode(marix(mode(marix(mo,mo),b),mode(a,b)),b);
   else return  mo=mode(marix(mo,mo),b);
 }
} 

————————————————————————————————————————————
总结:上面的包基本上包含了所有基本的操作(包括逻辑运算跟四则运算),中间的细节归大家理解了,这里不详述,所有代码均能在算法上证明是对的,但如果其中读者发现出错(注意在进行幂运算时出错的话,先检查是不是运算结果超过结构体内的数组所能储存的数字的大小,如果是这个原因的话可以将数组开大一点),可以跟博主联系。
之后会出一个博客,将操作转化为运算符重载,但是基本的原理跟此处是完全一样的了。

OK,大整数到此就结束一个段落了,有兴趣的读者还可以看一下我的博客里面的其它东西,会随着时间不断更新各种算法的讲解,由于博主的能力有限,也许里面有讲的不周到的地方,欢迎读者跟博主反馈。对于以上的内容有任何疑惑的读者可以向博主反馈,博主收到后会即时进行解答,也欢迎大家的提问,附上博主的邮箱:2766362964@qq.com

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值