用于表示为字符串的大十进制数的快速乘法的 Karatsuba 算法
给定两个数字字符串A和B ,任务是有效地找到两个数字字符串的乘积。
例子:
Input: A = 5678, B = 1234
Output: 7006652
Input: A = 74638463789, B = 35284567382
Output: 2633585904851937530398
方法:给定的问题可以使用 Karastuba 的快速乘法算法来解决,这个想法是在整数前面附加零,这样两个整数都有相等的偶数位数n 。此后,按以下方式划分数字:
A = Al * 10n/2 + Ar [Al and Ar contain leftmost and rightmost n/2 digits of A]
B = Bl * 10n/2 + Br [Bl and Br contain leftmost and rightmost n/2 digits of B]
- 因此,乘积A * B也可以表示为:
A * B = (Al * 10n/2 + Ar) * (Bl * 10n/2 + Br)
=> A * B = 10n*Al*Bl + 10n/2*(Al*Br + Bl*Ar) + Ar*Br
=> A * B = 10n*Al*Bl + 10n/2*((Al + Ar)*(Bl + Br) – Al*Bl – Ar*Br) + Ar*Br [since Al*Br + Bl*Ar = (Al + Ar)*(Bl + Br) – Al*Bl – Ar*Br]
请注意,上面的表达式只需要三个乘法Al*Bl 、 Ar*Br和(Al + Ar)*(Bl + Br) ,而不是标准的四个。因此,递归变为T(n) = 3T(n/2) + O(n)并且此递归的解为O(n 1.59 ) 。这个想法在这篇文章中得到了更彻底的讨论。因此,可以使用以下步骤解决上述问题:
- 创建一个函数findSum() ,该函数查找表示为字符串的两个大数的总和。类似地,创建一个函数findDiff() ,它找出两个表示为字符串的大数的差。
- 在使用 Karatsuba 算法将数字相乘的递归函数multiply(A, B)中,首先在A和B前面附加零,以使其位数相等且偶数。
- 然后计算上面定义的Al 、 Ar 、 Bl和Br的值,并递归地找到multiply(Al, Bl) 、 multiply(Ar, Br)和multiply((Al + Ar), (Bl + Br)的值)并将它们的值代入上述推导出的方程。
- 从方程中删除前导零后,打印方程的答案。
下面是上述方法的实现:
C++
// C++ progrram for the above approach
#include
using namespace std;
// Function to find the sum of larger
// numbers represented as a string
string findSum(string str1, string str2)
{
// Before proceeding further, make
// sure length of str2 is larger
if (str1.length() > str2.length())
swap(str1, str2);
// Stores the result
string str = "";
// Calculate length of both string
int n1 = str1.length();
int n2 = str2.length();
// Reverse both of strings
reverse(str1.begin(), str1.end());
reverse(str2.begin(), str2.end());
int carry = 0;
for (int i = 0; i < n1; i++) {
// Find the sum of the current
// digits and carry
int sum
= ((str1[i] - '0')
+ (str2[i] - '0')
+ carry);
str.push_back(sum % 10 + '0');
// Calculate carry for next step
carry = sum / 10;
}
// Add remaining digits of larger number
for (int i = n1; i < n2; i++) {
int sum = ((str2[i] - '0') + carry);
str.push_back(sum % 10 + '0');
carry = sum / 10;
}
// Add remaining carry
if (carry)
str.push_back(carry + '0');
// Reverse resultant string
reverse(str.begin(), str.end());
return str;
}
// Function to find difference of larger
// numbers represented as strings
string findDiff(string str1, string str2)
{
// Stores the result of difference
string str = "";
// Calculate length of both string
int n1 = str1.length(), n2 = str2.length();
// Reverse both of strings
reverse(str1.begin(), str1.end());
reverse(str2.begin(), str2.end());
int carry = 0;
// Run loop till small string length
// and subtract digit of str1 to str2
for (int i = 0; i < n2; i++) {
// Compute difference of the
// current digits
int sub
= ((str1[i] - '0')
- (str2[i] - '0')
- carry);
// If subtraction < 0 then add 10
// into sub and take carry as 1
if (sub < 0) {
sub = sub + 10;
carry = 1;
}
else
carry = 0;
str.push_back(sub + '0');
}
// Subtract the remaining digits of
// larger number
for (int i = n2; i < n1; i++) {
int sub = ((str1[i] - '0') - carry);
// If the sub value is -ve,
// then make it positive
if (sub < 0) {
sub = sub + 10;
carry = 1;
}
else
carry = 0;
str.push_back(sub + '0');
}
// Reverse resultant string
reverse(str.begin(), str.end());
// Return answer
return str;
}
// Function to remove all leading 0s
// from a given string
string removeLeadingZeros(string str)
{
// Regex to remove leading 0s
// from a string
const regex pattern("^0+(?!$)");
// Replaces the matched value
// with given string
str = regex_replace(str, pattern, "");
return str;
}
// Function to multiply two numbers
// using Karatsuba algorithm
string multiply(string A, string B)
{
if (A.length() > B.length())
swap(A, B);
// Make both numbers to have
// same digits
int n1 = A.length(), n2 = B.length();
while (n2 > n1) {
A = "0" + A;
n1++;
}
// Base case
if (n1 == 1) {
// If the length of strings is 1,
// then return their product
int ans = stoi(A) * stoi(B);
return to_string(ans);
}
// Add zeros in the beginning of
// the strings when length is odd
if (n1 % 2 == 1) {
n1++;
A = "0" + A;
B = "0" + B;
}
string Al, Ar, Bl, Br;
// Find the values of Al, Ar,
// Bl, and Br.
for (int i = 0; i < n1 / 2; ++i) {
Al += A[i];
Bl += B[i];
Ar += A[n1 / 2 + i];
Br += B[n1 / 2 + i];
}
// Recursively call the function
// to compute smaller product
// Stores the value of Al * Bl
string p = multiply(Al, Bl);
// Stores the value of Ar * Br
string q = multiply(Ar, Br);
// Stores value of ((Al + Ar)*(Bl + Br)
// - Al*Bl - Ar*Br)
string r = findDiff(
multiply(findSum(Al, Ar),
findSum(Bl, Br)),
findSum(p, q));
// Multiply p by 10^n
for (int i = 0; i < n1; ++i)
p = p + "0";
// Multiply s by 10^(n/2)
for (int i = 0; i < n1 / 2; ++i)
r = r + "0";
// Calculate final answer p + r + s
string ans = findSum(p, findSum(q, r));
// Remove leading zeroes from ans
ans = removeLeadingZeros(ans);
// Return Answer
return ans;
}
// Driver Code
int main()
{
string A = "74638463789";
string B = "35284567382";
cout << multiply(A, B);
return 0;
}
2633585904851937530398
时间复杂度: O(N log 3 ) 或 O(N 1.59 ),其中 N 是给定字符串A 和 B 的长度中的最大值。
辅助空间: O(N 2 )