大整数乘法(分治)

最近算法课要求实现大整数乘法,于是有了这篇博客。

思路

普通高精度乘法即模拟竖式乘法运算,一个数的每一位都与另一个数的每一位相乘,复杂度O(n^2),考虑分治,将一个大整数分成两部分,两部分分别处理后再相加;

设X=A*10^n/2+B ,Y=C*10^n/2+D,则XY=(A*10^n/2+B)(C*10^n/2+D)=AC*10^n+(AD+CB)*10^n/2+BD

但这样并没有优化时间复杂度,对AD+BC进行分解优化后得:

XY=AC*10^n+[(A-B)(D-C)+AC+BD]*10^n/2+BD;

每次要进行三次乘法,六次加减法,两次移位操作,复杂度降至O(n^1.59)。

代码:

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//大整数符号和数值分开处理 
struct node{
	string s; 
	int f,siz;
	node(){ s=""; f=1; siz=0;}
};

node add(node a,node b);
//初始化 
void init(node &a){
	if(a.s[0]=='-'){
		a.s.erase(a.s.begin()); a.f=-1;
	}
	else a.f=1;
	a.siz=a.s.size();
}
//打印函数 
void print(node x){
	int i=0;
	while(x.s[i]=='0') i++;
	if(i==x.siz){ cout<<0<<"\n"; return ;}
	if(x.f==-1) cout<<"-";
	for(;i<x.siz;i++) cout<<x.s[i];
	cout<<"\n";
}
//删除大整数前导零 
void lead_sub(node &a){
	while(a.siz>1&&a.s[0]=='0'){
		a.s.erase(a.s.begin()); a.siz--;
	} 
}
//将两个大整数通过补零,实现位数相同 
void lead_add(node &a,node &b){
	int cnt=max(a.siz,b.siz);
	reverse(a.s.begin(),a.s.end());
	while(a.siz<cnt){
		a.s+='0'; a.siz++;
	}
	reverse(a.s.begin(),a.s.end());
	reverse(b.s.begin(),b.s.end());
	while(b.siz<cnt){
		b.s+='0'; b.siz++;
	}
	reverse(b.s.begin(),b.s.end());
}
//减法 
node sub(node a,node b){
	node ans;
	b.f*=-1;
	if(a.f==b.f) return add(a,b);
	lead_sub(a); lead_sub(b);
	reverse(a.s.begin(),a.s.end());
	reverse(b.s.begin(),b.s.end());
	//判断数值部分大小 
	if(a.siz<b.siz) swap(a,b);
	if(a.siz==b.siz){
		for(int i=a.siz-1;i>=0;i--){
			if(a.s[i]>b.s[i]) break;
			if(a.s[i]<b.s[i]){
				swap(a,b); break;
			}
		}
	}
	ans.f=a.f;
	int jin=0,i;
	for(i=0;i<b.siz;i++){
		int tmp=(a.s[i]-'0')-(b.s[i]-'0');
		if(tmp-jin<0){
			ans.s+='0'+tmp-jin+10; jin=1;
		}
		else{
			ans.s+='0'+tmp-jin; jin=0;
		}
	}
	for(;i<a.siz;i++){
		int tmp=(a.s[i]-'0');
		if(tmp-jin<0){
			ans.s+='0'+tmp-jin+10; jin=1;
		}
		else{
			ans.s+='0'+tmp-jin; jin=0;
		}
	}
	reverse(ans.s.begin(),ans.s.end());
	lead_sub(ans);
	ans.siz=ans.s.size();
	return ans;
}
//加法 
node add(node a,node b){
	node ans; 
	if(a.f!=b.f){ b.f*=-1; return sub(a,b);}
	lead_add(a,b);
	ans.f=a.f;
	reverse(a.s.begin(),a.s.end());
	reverse(b.s.begin(),b.s.end());
	int jin=0,i;
	for(i=0;i<a.siz&&i<b.siz;i++){
		int tmp=(a.s[i]-'0')+(b.s[i]-'0');
		if(tmp+jin>=10){
			ans.s+='0'+tmp+jin-10; jin=1;
		}
		else{
			ans.s+='0'+tmp+jin; jin=0;
		}
	}
	for(;i<a.siz;i++){
		int tmp=(a.s[i]-'0');
		if(tmp+jin>=10){
			ans.s+='0'+tmp+jin-10; jin=1;
		}
		else{
			ans.s+='0'+tmp+jin; jin=0;
		}
	}
	for(;i<b.siz;i++){
		int tmp=(b.s[i]-'0');
		if(tmp+jin>=10){
			ans.s+='0'+tmp+jin-10; jin=1;
		}
		else{
			ans.s+='0'+tmp+jin; jin=0;
		}
	}
	if(jin) ans.s+='0'+1;
	reverse(ans.s.begin(),ans.s.end());
	lead_sub(ans);
	ans.siz=ans.s.size();
	return ans;
}
//判断该数是否为零 
bool check(node a){
	lead_sub(a);
	if(a.s=="0") return true;
	return false;
}
//乘法 
node mul(node A,node B){
	node ans;
	lead_add(A,B);
	ans.f=A.f*B.f;
	//若其中一个数位0,直接返回0 
	if(check(A)||check(B)){ans.s="0"; ans.siz=1; return ans;}
	//位数为1,直接计算 
	if(A.siz==1){
		int tmp=(A.s[0]-'0')*(B.s[0]-'0');
		if(tmp/10){ ans.s+='0'+tmp/10; ans.siz++;}
		ans.s+='0'+tmp%10; ans.siz++;
		return ans;
	}
	lead_add(A,B);
	int len=A.siz/2;
	node a,b,c,d,e,f;
	a.f=b.f=c.f=d.f=1;
	//拆分 
	a.s=A.s.substr(0,len);
	b.s=A.s.substr(len);
	c.s=B.s.substr(0,len);
	d.s=B.s.substr(len);
	init(a); init(b); init(c); init(d);
	//共进行三次乘法,六次加减法,两次移位操作 
	e=sub(a,b);
	f=sub(d,c);
	a=mul(a,c);  
	e=mul(e,f);
	b=mul(b,d);
	e=add(e,a);
	e=add(e,b);
	for(int i=1;i<=2*(A.siz-len);i++){
		a.s+='0'; a.siz++;
		if(i<=A.siz-len){ e.s+='0'; e.siz++;}
	}
	ans=add(a,e); ans=add(ans,b);
	ans.f*=A.f*B.f;
	return ans;
}

int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	node a,b;
	cin>>a.s>>b.s;
	init(a); init(b);
	node ans=mul(a,b);
	print(ans);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值