问题描述:如标题所示,就是两个大整数相乘,有时候如果数值过大,计算机是不能直接计算出来的,就需要使用到分支的思想。
分治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解。即一种分目标完成程序算法,一般情况都会用二分来解决(网上资料很多,此处不再赘述)。
情景假设:假设有两个大整数X、Y,分别设X=1234、Y=6789。现在要求X*Y的乘积,小学的算法就是把X与Y中的每一项去乘,但是这样的乘法所需的时间复杂度为O(N^2),(因为每一位要逐个去乘),所以效率比较低下。那我们可以采用分治的算法,将X、Y拆分成四部分,如下图:
这样X可以表示为
Y同理,所以Y=C*10^(n/2)+D;
(有的资料书上写的是X=A**2^(n/2)+B,因为它是表示的二进制,此处写的是十进制。)
所以现在将一个大的整数分成了两部分,问题规模减小,这样直接相乘就会写成
但是这样你会发现,最后还是要计算4次n/2位整数的乘法,以及三次加法,根据master定理最后会发现T(n)=O(n^2);所以说这其实没有改进成功,要改进成功,就需要通过将算式变形来减少乘法的次数,对此,我们写成如下形式:
这样就能减少乘法的次数,(只做了AC,BD (A-B)(D-C)三次乘法),由master定理可得T(n)=O(n^log3),优化成功。
测试代码如下:
#include<cstdio>
#include<cmath>
using namespace std;
#define SIGN(A) ((A > 0) ? 1 : -1)
int divideConquer(int X, int Y, int n) {
int sign = SIGN(X) * SIGN(Y);
int x = abs(X);
int y = abs(Y);
if (x == 0 || y == 0) {
return 0;
}
else if (n == 1) {
return sign * x * y;
}
else {
int A = (int)x / pow(10, (int)(n / 2));
int B = x - A * pow(10, n / 2);
int C = (int)y / pow(10, (int)(n / 2));
int D = y - C * pow(10, n / 2);
int AC = divideConquer(A, C, n / 2);
int BD = divideConquer(B, D, n / 2);
int ABDC = divideConquer((A - B), (D - C), n / 2) + AC + BD;
return sign * (AC * pow(10, n) + ABDC * pow(10, (int)(n / 2)) + BD);
}
}
int main() {
int x, y, n;
scanf_s("%d%d%d", &x, &y, &n);
printf("x 和 y的乘积为:%d", divideConquer(x, y, n));
}