分治思想—递归实现大数乘法ACM(含奇数长度处理)

来源:互联网 发布:淘宝网的特点 编辑:程序博客网 时间:2024/06/05 22:57

一般的算法都是O(n^2)这里不再给出。
但是如果使用递归的话复杂度会变为O(n^log3),主要的做法是减少运算次数
对于任意两个数字X,Y
可以将其在中间部分 分解为两部分例如:
1234 -> 12 和 34
5678 -> 56 和 78
(细心的人可能发现就是1234 = 12*10^2 + 34)那么对于任意一个数都可以分解为前后两部分。
我们还是以1234这样的四位数字为例,设1234分解A、B,5678分解为C、D按照一般思路对于乘法有 (以下为竖式)
这里写图片描述
同时对于形如1234结果可以写为:A*C*10^4+(AD+BC)*10^2+BD
观察那个竖式和这里的式子是不是有一定的规律?
那么1234可以拆为AB(每个字母两位)同样对于12 和34可以拆为A、B(每个字母一位)这样就可以进行递归了。
可以发现这样的复杂度为O(n方)。
下面通过一个小技巧进行化简:首先有:
XY
=(A∗10^(n^2)+B)(C∗10^(n^2)+D)
=AC∗10^n+(AD+BC)∗10(n^2)+BD——————————–(2)
=AC∗10^n+((A+B)(D+C)-AC-BD)∗10^(n^2)+BD —————-(3)
注意第2行到第3行的变化:(AD+BC)∗10(n^2)=((A+B)(D+C)-AC-BD)∗10^(n^2)
其中中间最大括号里面的AC BD 在外面算过,此时加上 (A−B)(D−C)一共只需要三次。而之前每层递归需要四次。
改进后又有:
T(n) =
{O(1),3T(n2)+O(n),n =1 n>1
下面给出模版

#include <iostream>#include <cmath>#include <algorithm>using namespace std;long long multi(long long p , long long  q, int len) {    if (p == 0 || q == 0)return 0 ;    if (len == 1)return p * q;    else {        long long danwei  =  pow (10, len);        long long danwei2 = pow (10,  len / 2);        long long a1  =  p / danwei2;        long long a2  =  p % danwei2;        long long b1  =  q / danwei2;        long long b2  =  q % danwei2;        // printf("1: %lld\n", a1  );        // printf("2: %lld\n", a2  );        // printf("3: %lld\n", b1  );        // printf("4: %lld\n", b2  );        long long temp1 = multi(a1, b1, len / 2);        long long temp3 = multi(a2, b2, len / 2);        long long temp2 = multi(a1+a2, b1+b2, len / 2) - temp3 - temp1;        return (temp1 * danwei + temp2 * danwei2 + temp3);    }}int main(int argc, char const *argv[]){    long long a , b;    scanf("%lld", &a);    scanf("%lld", &b);    // printf("->%lld\n", a);    // printf("->%lld\n", b);    int index = 0,index2=0;    int c  = a ;    while (c) {        c /= 10;        index ++;    }    c  = b ;    while (c) {        c /= 10;        index2 ++;    }    cout << multi(a, b, max(index2,index));    return 0;}

大家可能发现这样的写法对于奇数没法处理:
下面只要讨论奇数情况就可以:
附上完整代码:

#include <iostream>#include <cmath>#include <algorithm>using namespace std;long long multi(long long p , long long  q, int len) {    if (p == 0 || q == 0)return 0 ;    if (len == 1)return p * q;    long long danwei  =  pow (10, len);    long long danwei2 = pow (10,  len >> 1 );//这个是除以2    long long a1  =  p / danwei2;    long long a2  =  p % danwei2;    long long b1  =  q / danwei2;    long long b2  =  q % danwei2;    if ((len & 1) == 1) //尝试一下位运算,如果为奇数  123 123这类    {        // printf("1: %lld\n", a1  );        // printf("2: %lld\n", a2  );        // printf("3: %lld\n", b1  );        // printf("4: %lld\n", b2  );        long long temp1 = multi(a1, b1, (len >> 1) + 1);        long long temp3 = multi(a2, b2, (len >> 1));        long long temp2 = multi(a1 + a2, b1 + b2, (len >> 1) + 1) - temp3 - temp1;        danwei  =  pow (10, len - 1);        return (temp1 * (danwei) + temp2 * danwei2 + temp3);    }    else {        long long temp1 = multi(a1, b1, len >> 1);        long long temp3 = multi(a2, b2, len >> 1);        long long temp2 = multi(a1 + a2, b1 + b2, len >> 1) - temp3 - temp1;        return (temp1 * danwei + temp2 * danwei2 + temp3);    }    return 0;}int main(int argc, char const *argv[]){    long long a , b;    scanf("%lld", &a);    scanf("%lld", &b);    // printf("->%lld\n", a);    // printf("->%lld\n", b);    int index = 0, index2 = 0;    int c  = a ;    while (c) {        c /= 10;        index ++;    }    c  = b ;    while (c) {        c /= 10;        index2 ++;    }    if (index != index2)        cout << "您输入的长度不相等" << endl;    else        cout << multi(a, b, max(index2, index));    return 0;}