Karatsuba 算法

算法思想:

普通的乘法算法使用循环累加处理进位的方法,时间复杂度为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]<<' ';
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值