递归分治-大整数乘法

最近在学算法,想着不能只是学,要深刻领悟,需要记录,需要写代码,需要分析……所以就诞生了这篇博客。

问题描述:

设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=A10n2+B
Y=C10n2+D
这样XY的乘积就是
XY=(A10n2+B)C10n2+D=AC10n+AD+BC10n2+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=A10n2+BC10n2+D=AC10n+AD+BC10n2+BD=AC10n+((AB)(DC)+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;
}

综上所述,主要是掌握对算法复杂度的推导,以及实现相应的算法。在这里,不得不感叹数学家的厉害,只是一个小小的公式变换,就将复杂度大大降低了。请大数学家们收下我的膝盖,哈哈~

    原文作者:递归与分治算法
    原文地址: https://blog.csdn.net/u013266600/article/details/72566605
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞