算法思想:
普通的乘法算法使用循环累加处理进位的方法,时间复杂度为O(n^2)
Karatsuba算法通过数学技巧,将时间复杂度降至O(n^lg3),具体如下:
设a,b为10位的整数,将a,b分别分成两个部分,即
a = a1 * 10^5 + a0
b = b1 * 10^5 + b0
通过这种变化
a * b = a1 * b1 10^10 + (a1 * b0 + a0 * b1) * 10^5 + a0 * b0
再设
z0 = a0 * b0
z1 = a1 * b0 + a0 * b1
z2 = a1 *b1
利用 (a0 + a1) * (b0 + b1) = a0*b0 + a1 * b0 + a0 b1 + a0*b0 可求得 z1
z1 = (a0 + a1) * (b0 + b1) - a0*b0 - a0*b0
其中 (a0 + a1) * (b0 + b1) ,a0*b0 ,a0*b0 可通过递归上述过程实现
时间复杂度:
假设a,b的位数 n 是 2^k 形式,递归分割了k次,每一次调用都会增加3个子问题,一共有 3^k 个子问题,每个子问题需要1次相乘,即相乘总数为 O(3^k),由于 n = 2^k ,即,k = lg(n),所以时间复杂度为O(3^lg(n)) = O(n^lg(3))
代码:
#include<iostream>
using namespace std;
#include<vector>
#include<math.h>
vector<int> a;
vector<int> b;
void input(vector<int> &in){
int num;
scanf("%d",&num);
int a;
for(int i=0;i<num;i++){
cin>>a;
in.push_back(a);
}
}
// 向量相加
vector<int> Add(vector<int> &a,vector<int> &b,int k){
for(int i=0;i<k;i++) b.push_back(0); //b*10^k
int asize = a.size();
int bsize = b.size();
if(asize<bsize) return Add(b,a,0); //a比b短
int flag,n,diff;
flag = 0; //进位
diff = asize - bsize; //相差位数
for(int i=asize-1;i>=0;i--){ //从最后一位开始加
if(i-diff >= 0)n = a[i]+b[i-diff]+flag;
else n=a[i]+flag;
flag = n/10;
a[i] = n%10;
}
if(flag > 0){ //处理最高位进位
a.push_back(1);
for(int i=asize;i>=1;i--){
a[i] = a[i-1];
}
a[0] = flag;
}
return a;
}
// 向量相减
vector<int> Sub(vector<int> &a,vector<int> &b){
int asize = a.size();
int bsize = b.size();
if(asize<bsize) return Sub(b,a); //a比b短
int flag,n,diff;
flag = 0;
diff = asize - bsize; //相差位数
for(int i=asize-1;i>=0;i--){
if(i-diff >= 0)n = a[i]-b[i-diff]+flag;
else n=a[i]+flag;
if(a[i]-b[i-diff] < 0 && i-diff >=0){
flag = -1;
a[i] = 10+n;
}
else{
flag = 0;
a[i] = n;
}
}
int cntzero = 0; //高位的零的个数
for(int i=0;i<a.size();i++){
if(a[i] == 0) cntzero++;
else break;
}
for(int i=0;i<a.size();i++) //消除高位的零
a[i] = a[i+cntzero];
for(int i=0;i<cntzero;i++)//删除多余部分
a.pop_back();
return a;
}
//向量乘法
vector<int> Multi(vector<int> &a,vector<int> &b){
if(a.size()>b.size()) return Multi(b,a); //a比b大
vector<int> v(a.size() + b.size() +1); //结果数组 v
int diff = b.size()-a.size();
for(int i=0;i<a.size();i++){ //循环相加
for(int j=0;j<b.size();j++){
v[i+j] += a[a.size()-1-i] * b[b.size()-1-j];
}
}
for(int i=0;i<v.size();i++){
if(v[i]<0){
int borrow = (abs(v[i]) + 9) /10;
v[i+1] -= borrow;
v[i] +=borrow*10;
}
else{
v[i+1] += v[i] /10;
v[i] %= 10;
}
}
while(v.size() > 1&&v.back() == 0) v.pop_back();//去除 v 多于部分
vector<int> c(v.size());
for(int i=0;i<v.size();i++){
c[i] = v[v.size()-1 - i]; //倒序输出
}
return c;
}
// Karatsuba算法
vector<int> Karatsuba(vector<int> &a,vector<int> &b){
int asize = a.size();
int bsize = b.size();
if(asize <2 || bsize <2) return Multi(a,b); //长度小于2 使用普通乘法
if(asize < bsize) return Karatsuba(b,a); //保证 a长度比b大
if(asize == 0 || bsize == 0) return vector<int>();
int half = asize / 2; //去一半
// a 分成 a1(高位),a0(低位)
vector<int> a1(a.begin(),a.end()-half);
vector<int> a0(a.end()-half,a.end());
//同理 b
vector<int> b1(b.begin(),b.end()-min(half,bsize));
vector<int> b0(b.end()-min(half,bsize),b.end());
vector<int> z0 = Karatsuba(a0,b0);
vector<int> z2 = Karatsuba(a1,b1);
vector<int> tmpa = Add(a0,a1,0),tmpb = Add(b0,b1,0);
vector<int> z1 = Karatsuba(tmpa,tmpb);
z1 = Sub(z1,z0);
z1 = Sub(z1,z2);
vector<int> ret;
ret.push_back(0);
ret = Add(ret,z0,0);
ret = Add(ret,z1,half);
ret = Add(ret,z2,half*2);
return ret;
}
int main(){
input(a);
input(b);
vector<int> v = Karatsuba(a,b);
int n = v.size();
for(int i=0 ;i<n;i++)
cout<<v[i]<<' ';
}