20200924
原文链接:https://www.cnblogs.com/little-kwy/archive/2017/09/30/7613642.html
分治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解。即一种分目标完成程序算法,简单问题可用二分法完成。
简而言之,分治,其实就是分而治之,将一个大问题划分成小问题,再将这些小问题不断再不断的划分,依次重复,直至分成可以解决的小问题。
分治法的重点是分析问题是否可以划分为规模较小的子问题,难点是如何划分以及划分之后如何将各个子问题的解合并成最终的解。这一般需要用到数学知识或者其他理论。如下图:
(理想状态下,即X与Y相同)
现假设我们有两个大整数 X,Y,分别设为x=1234,Y=5678,需要求两个数的乘积XY。按照小学所学的方法去计算乘积,需要的时间复杂度为O(n2),运算效率太低。我们这里试着采用分治思想,将n位X和Y都分为2段,每段长度为n/2位。如下图:
注:我们这里取的大整数X、Y是在理想状态下,即X与Y的位数一致,且
在(1)式中,我们一共需要进行4次n/2的乘法(AC2次,AD、BC各一次)和3次加法,因而该算法的时间复杂度为:
通过master定理可以求得,跟小学算法的时间复杂度没有区别。
但是我们再来看看,我们是否可以用加法来换取乘法?因为多一个加法操作,也是常数项,对时间复杂度没有影响,如果减少一个乘法则不同。
(1)式化为:
#include
//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 argc, char* argv[])
{
int x, y, n;
scanf("%d%d%d", &x, &y, &n);
printf("x 和 y的乘积为:%d", divideConquer(x, y, n));
return 0;
}
运行结果: