最近算法课要求实现大整数乘法,于是有了这篇博客。
思路
普通高精度乘法即模拟竖式乘法运算,一个数的每一位都与另一个数的每一位相乘,复杂度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;
}