C++ 大整数运算 高精度除法

前言

这篇文章主要是对于大整数类的设计过程中,如何实现并改进长除法(模拟竖式法)的一个总结。

高精度除法的分类和比较

虽然有些文章在讨论大整数的除法运算时,喜欢分成高精度除以高精度高精度除以低精度(短除法)两种情况并分开实现,但在本文中将不做这种区分。事实上,短除法就是长除法的一个缩略。
按照wiki百科,除法大致可以分为以下两类:

  • 慢速算法:每步只能确定一位的算法。长除法属于此类,时间复杂度为O(N2)。
  • 快速算法:每步可以确定多位的算法。牛顿迭代法属于此类,配合用FFT实现的乘法,时间复杂度为O(NlogN)。

一些讨论

这里讨论的是为了实现大整数类所必需的一些基础。厘清它们对提升算法的效率是有意义的。

1. 关于底层

标准C++的语法中基本的数据类型都带有基本的四则运算,这里的底层主要指硬件或者编译器。
要实现大整数的除法运算,至少需要用到加、减两种基本运算,乘、除和取余虽然不是必须的,但我们仍然假定底层(硬件或者软件模拟)已经实现,因为后面的优化将用到它们。

2. 关于数据结构

数据结构有多种实现方式,互有优劣。数组最简单,但长度固定,需要考虑溢出。STL的vector不用担心溢出,但不是线程安全的。链表线程安全,但操作复杂且效率低下。
本文中我们将使用数组,其中数组的低位存储数值的低位,数组的最高位存储数值的符号。

3. 关于数据类型

数据类型的选择,取决于存储效率和处理能力。用1个int型存储1个十进制有效数字显然是低效的,但用long型存储9个十进制有效数字也未必最佳(假如系统是16位的,则long型的四则运算本身就需要软件模拟)。有些类型的大小也依赖于编译器的实现,这也是在设计过程中需要考虑的。
本文先从1个char型存储1位十进制有效数字开始设计,暂将优化(压位)放到后面讨论。这样的优点是可以使用字符串进行存储,在输入输出上有所便宜。

长除法的实现

1. 除法的基本原理

本质上,慢速算法都是通过减法来实现除法。不考虑效率的话,下面的代码就可以实现除法。

Integer Integer::operator/(const Integer& itg)const{
   
	//...
	//prod=*this;
	//div=itg;
	result=zero;
	while(!(prod<div)){
   
		prod=prod-div;
		result=result+one;
	}
	//...
}

显然,这个实现的时间复杂度是指数级的。现实中大概只有幼儿园的小朋友会使用。

2. 优化策略

如何高效率地完成减法,是优化的一个方向。直观的办法是通过减去除数的某个已知的倍数,来达到提高效率的目的。下面的代码是一个最简单的实现。

Integer Integer::operator/(const Integer& itg)const{
   
	//...
	//prod=*this;
	//div=itg;
	result=zero;
	quot=one;
	base=div;
	while(!(prod<div)){
   
		if(prod<base){
   
			quot=one;
			base=div;
		}else{
   
			prod=prod-base;
			result=result+quot;
			quot=quot+quot;
			base=base+base;
		}
	}
	//...
}

这个实现的时间复杂度已经达到O(N2),但无奈常数太大。因为每次采用的是翻倍,所以这里计算N是以2为底取的对数,而常用的笔算方法是以10为底取的对数。而且每次计算都要调用大整数的加减法,这也是一笔不小的开销。

3. 长除法的直减版本

下面的代码是采用移位策略的实现,已是相当接近于列竖式笔算的版本。

Integer Integer::operator/(const Integer& itg)const{
   
	Integer result=0,prod,div,base;
	int loop,offset,quot,reg,trans=0;

	/*处理除0异常*/

	prod=*this;
	if(*(prod.p+Length-1)=='-') *(prod.p+Length-1)='+';
	div=itg;
	if(*(div.p+Length-1)=='-') *(div.p+Length-1)='+';
	while(!(prod<div)){
   
		base=div;
		quot=1;

		//确定移位的具体值
		loop=prod.index-1;
		offset=base.index-1;
		while(*(prod.p+loop)==*(base.p+offset)){
   
			if(offset>0){
   
				loop--;
				offset--;
			}else break;
		}
		if(char2int(*(prod.p+loop))>=char2int
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值