关于karatsuba算法在c++的实现

我想实现一个karatsuba方法,将l1和l2用karatsuba算法相乘得到乘积。B是karatsuba的基数。l1,l2都是最高100位数的数字。

我的karatsuba方法:

// karatsuba multiplication
long long karastuba(long long x, long long y, long long B) {
    // 如果x,y是个位数
    if (x < B || y < B) {
        return x * y;
    }

    // 计算x和y的位数
    long long n = max(to_string(x).size(), to_string(y).size());
    long long mid = n / 2;

    // 分开为两部分
    long long a = x / pow(B, mid);
    long long b = x % (long long)pow(B, mid);
    long long c = y / pow(B, mid);
    long long d = y % (long long)pow(B, mid);

    // 卡拉楚巴方法计算
    long long ac = karastuba(a, c, B);
    long long bd = karastuba(b, d, B);
    long long ad_bc = karastuba(a + b, c + d, B) - ac - bd;

    long long res = 0;
    for (int i = 0; i < 2 * mid; i++) {
        res += ac * pow(B, 2 * mid + i);
    }
    for (int i = 0; i < mid; i++) {
        res += ad_bc * pow(B, mid + i);
    }
    res += bd;

    return res;
}

我的main function:

int main() {
    long long I1, I2, B;

    cin >> I1 >> I2 >> B;

    // karatsuba function
    long long product = karastuba(I1, I2, B);

    cout << product << endl;

    return 0;
}
我的输入:
10211001033220013020313211003303321300100220101132012012132211000303113013132010113123003302 311011200232012231 4

我希望的输出:

3303003100312021130111131112011212313232020311201200210322131301032332211032003230122333022213200232110112022

我得到的输出:

-9223018708791985204

我没有懂我哪一部分有错,在这个问题上已经试了一下午了还是没有找到问题出在哪。

参考GPT和自己的思路:

你的代码存在一些问题。首先,在第9行,你使用了 to_string() 函数来计算两个数字的位数,但该函数的时间复杂度是 O(n),会使你的程序变得非常缓慢。更好的方法是通过循环除以基数 B,直到商为 0,计算数字的位数。

其次,在第12行和第13行,你错误地把第一个数字拆分成了两部分,而其中一部分应该是第二个数字。具体地说,你应该把 x 拆分成 ab^mid + c 和 y 拆分成 db^mid + e,然后对乘积进行以下操作:

ac * B^(2*mid) + ((a+b)(c+d)-ac-bd)*B^mid + bd

因此,第19行到21行的计算结果不正确。你需要改写这三行代码,仔细检查一下。

最后,在第25行和第28行,你使用了 pow() 函数来求幂次方,但该函数的时间复杂度也非常高,会使你的程序变得很慢。更好的方法是通过预处理一个数组,存储不同幂次方的值,然后通过索引来访问该数组。这样可以将时间复杂度降低到 O(1)。

下面是修正后的代码:

#include <iostream>
#include <cstring>
using namespace std;

long long pow(long long x, long long y, long long B) {
    long long res = 1;
    for (int i = 0; i < y; i++) {
        res *= B;
    }
    return res * x;
}

long long karatsuba(long long x, long long y, long long B, int n) {
    if (n == 1) {
        return x * y;
    }
    int mid = n / 2;
    long long a = x / pow(B, mid);
    long long b = x % pow(B, mid);
    long long c = y / pow(B, mid);
    long long d = y % pow(B, mid);
    long long ac = karatsuba(a, c, B, mid);
    long long bd = karatsuba(b, d, B, mid);
    long long ad_bc = karatsuba(a - b, c - d, B, mid);
    return ac * pow(B, 2 * mid) + (ad_bc + ac + bd) * pow(B, mid) + bd;
}

int main() {
    string s1, s2;
    long long B;
    cin >> s1 >> s2 >> B;
    int n = max(s1.size(), s2.size());
    if (n > 100) {
        cout << "Error: the input numbers are too large." << endl;
        return 0;
    }
    long long x = 0, y = 0;
    for (int i = 0; i < s1.size(); i++) {
        x += (s1[i] - '0') * pow(B, s1.size() - i - 1);
    }
    for (int i = 0; i < s2.size(); i++) {
        y += (s2[i] - '0') * pow(B, s2.size() - i - 1);
    }
    long long product = karatsuba(x, y, B, n);
    string res = "";
    while (product > 0) {
        res.push_back('0' + product % B);
        product /= B;
    }
    if (res == "") {
        res = "0";
    }
    reverse(res.begin(), res.end());
    cout << res << endl;
    return 0;
}

注意,这里的乘法和求幂次方都是手动实现的,因此可能比使用内置函数慢。如果需要更高效的实现,在处理大整数时应该使用更高效的算法,例如 FFT。