递归分治-大整数乘法
2017-05-19 22:34
162 查看
最近在学算法,想着不能只是学,要深刻领悟,需要记录,需要写代码,需要分析……所以就诞生了这篇博客。
第一步:6*8;
第二步:5*8;
第三步:6*7;
第四步:5*7;
第五步:以上4步的和加起来,得到最后的结果。
将以上的例子进行抽象,即可以这样表示:
将n位十进制(还包括其他进制,如二进制、八进制、十六进制等)整数X和Y都分为2段,每段的长为n/2位。即:
X=A∗10n2+B
Y=C∗10n2+D
这样XY的乘积就是
XY=(A∗10n2+B)∗(C∗10n2+D)=AC∗10n+(AD+BC)∗10n2+BD
由以上的等式我们可以看出,需要4次乘法,分别是:AC,AD,BC,BD,以及3次加法,还有3次移位操作。所有这些加法和移位公用O(n)步运算。设T(n)是2个n位整数相乘所需的运算总数,则有:
T(n) =
{O(1),4T(n2)+O(n),n =1 n>1
然后,我们进行计算,得到该算法的时间复杂度为O(n^2),具体过程如下:
实现代码如下:
XY=(A∗10n2+B)(C∗10n2+D)=AC∗10n+(AD+BC)∗10n2+BD=AC∗10n+((A−B)(D−C)+AC+BD)∗10n2+BD
通过以上表达式变形,我们只需进行3次n/2位整数的乘法,6次加减法操作和2次移位。由此可得如下的表达式:
T(n) =
{O(1),3T(n2)+O(n),n =1 n>1
再次进行计算,得到该算法的时间复杂度为O(nlog3),相比于O(n2)来说,这是一个很大的改进。
代码如下,这个代码是在别人的基础上改了一下,具体如下:
综上所述,主要是掌握对算法复杂度的推导,以及实现相应的算法。在这里,不得不感叹数学家的厉害,只是一个小小的公式变换,就将复杂度大大降低了。请大数学家们收下我的膝盖,哈哈~
问题描述:
设X和Y都是n位整数,计算它们的乘积XY。可以使用传统的数学计算方法,但是这样做计算步骤太多,效率较低。如果将每个一位数的乘法或加法看做一步运算,则这种方法需要进行O(n^2)步运算才能求出乘积XY。方法一
基本计算步骤如下,如:56*78第一步:6*8;
第二步:5*8;
第三步:6*7;
第四步:5*7;
第五步:以上4步的和加起来,得到最后的结果。
将以上的例子进行抽象,即可以这样表示:
将n位十进制(还包括其他进制,如二进制、八进制、十六进制等)整数X和Y都分为2段,每段的长为n/2位。即:
X=A∗10n2+B
Y=C∗10n2+D
这样XY的乘积就是
XY=(A∗10n2+B)∗(C∗10n2+D)=AC∗10n+(AD+BC)∗10n2+BD
由以上的等式我们可以看出,需要4次乘法,分别是:AC,AD,BC,BD,以及3次加法,还有3次移位操作。所有这些加法和移位公用O(n)步运算。设T(n)是2个n位整数相乘所需的运算总数,则有:
T(n) =
{O(1),4T(n2)+O(n),n =1 n>1
然后,我们进行计算,得到该算法的时间复杂度为O(n^2),具体过程如下:
实现代码如下:
#include<stdio.h> #include<math.h> #define MAX_LENGTH 10 void sqperateNum(int num, int seArray_num[]); //数据存储到数组中 int main() { int i=0, j=0; int num1, num2; int sum = 0; int seArray_num1[MAX_LENGTH] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; //初始化数组 int seArray_num2[MAX_LENGTH] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; printf("Please input two numbers:\n"); scanf("%d%d", &num1, &num2); sqperateNum(num1, seArray_num1); sqperateNum(num2, seArray_num2); while (seArray_num1[i]!=-1) { while (seArray_num2[j] != -1) { sum += seArray_num1[i] * seArray_num2[j] * pow(10.0, (i + j)); j++; } i++; j = 0; } printf("%d*%d=%d\n", num1, num2, sum); return 0; } //数据存储到数组中 void sqperateNum(int num, int seArray_num[]) { int k = 0; do { seArray_num[k] = num % 10; num /= 10; k++; } while (num%10!=0 ||(num/10 !=0)); }
方法二
然后我们对于该问题进行优化,要想改进该算法的复杂性,必须减少乘法的次数。我们对以上的表达式进行一个优化,得到如下的表达式:XY=(A∗10n2+B)(C∗10n2+D)=AC∗10n+(AD+BC)∗10n2+BD=AC∗10n+((A−B)(D−C)+AC+BD)∗10n2+BD
通过以上表达式变形,我们只需进行3次n/2位整数的乘法,6次加减法操作和2次移位。由此可得如下的表达式:
T(n) =
{O(1),3T(n2)+O(n),n =1 n>1
再次进行计算,得到该算法的时间复杂度为O(nlog3),相比于O(n2)来说,这是一个很大的改进。
代码如下,这个代码是在别人的基础上改了一下,具体如下:
#include<stdio.h> #include<math.h> #include<stdlib.h> int IntegerMultiply(int X, int Y, int N) { int x = X; int y = Y; if ((0 == x) || (0 == y)) return 0; if (1 == N) { return x*y; } else { int XL = x / (int)pow(10., (int)N / 2); int XR = x - XL * (int)pow(10., N / 2); int YL = y / (int)pow(10., (int)N / 2); int YR = y - YL * (int)pow(10., N / 2); int XLYL = IntegerMultiply(XL, YL, N / 2); int XRYR = IntegerMultiply(XR, YR, N / 2); int XLYRXRYL = IntegerMultiply(XL - XR, YR - YL, N / 2) + XLYL + XRYR; return (XLYL * (int)pow(10., N) + XLYRXRYL * (int)pow(10., N / 2) + XRYR); } } int main() { int x = 1234, y = 9876; int value = 0; value = IntegerMultiply(x, y, 4); printf("%ld*%ld=%ld", x,y,value); return 0; }
综上所述,主要是掌握对算法复杂度的推导,以及实现相应的算法。在这里,不得不感叹数学家的厉害,只是一个小小的公式变换,就将复杂度大大降低了。请大数学家们收下我的膝盖,哈哈~
相关文章推荐