大整数相乘

一、问题描述

计算两个很大整数的结果,例如:27392361983108271361039746313 * 37261038163103818366341087632113

二、算法分析

先用一个简单的例子,如 234 * 456 = ???来考虑。在这里考虑将 456 拆分为 4、5、6,然后分别去乘以 234。

                                       234

                                x      456

                            --------------------

                                      1404

                                     1170

                                     936 

                            --------------------

                                    106704

这就是熟悉的竖式计算乘法的结构,看着每一步计算出来的结果,一层一层的,是否看起来像二维数组?我们就用二维数组来保存结果,数组的第一行保留 1404,第二行保留 11700,第三行保留 93600。由于不能直接存储,需要对存放的位置做一下计算:数组该有多少行,该有多少列?

在这里我们需要知道,3 位 * 3 位,结果最多为 6 位数;2 位 * 6 位,结果最多为 2 + 6 = 8 位,所以这里数组该有 6 列,而对于行数,则由被乘数决定,所以这里为 3 行。

temp[3][6] = { 0  0  1  4  0  4

                       0  1  1  7  0  0

                       0  9  3  6  0  0  }

每列依次往下加 1 0 6 7 0 4; 所得刚好为我们要的答案。

三、代码实现

时间复杂度 O(n2)

#include <stdio.h>

#define num1  11
#define num2  6

void MultiOfLargeNumbers(int a1[], int a2[])
{
    int temp[num2][num1 + num2] = { 0 };  // 注意:二维数组列数的规律
    int x, y;  // x - 行数,y - 列数
    uint isCarry = 0; // 进位值
    
    // 打印二维数组
    for (int i = 0; i < num2; i++) { // 行
        for (int j = 0; j < num1 + num2; j++)  // 列
            printf("%d  ", temp[i][j]);
        printf("\n");
    }
    printf("\n");
    
    for(int idx2 = num2 - 1; idx2 >= 0; idx2--) {  // 乘数
        x = num2 - idx2 - 1;
        y = num1 + idx2;
        
        for(int idx1 = num1 - 1; idx1 >= 0; idx1--, y--) {  // 被乘数
            // 加上进位数值
            temp[x][y] = a1[idx1] * a2[idx2] + isCarry;
            
            isCarry = 0;
            
            // 当前计算结果需要进位,计算进位数值和结果数值
            if(temp[x][y] >= 10) {
                isCarry = temp[x][y] / 10;
                temp[x][y] %= 10;
            }
        }
        
        if(isCarry) {
            // 首位有进位
            temp[x][y] += isCarry;
            isCarry = 0;
        }
    }
    
    // 合并
    int temp_sum[num1 + num2] = {0};
    // 将每一列的数组到最后的结果数组里面
    for(int j = num2 + num1 - 1; j >= 0; j--) {  // 列
        
        for(int i = num2 - 1; i >= 0; i--) {  // 行
            temp_sum[j] += temp[i][j];
        }
        if (isCarry) {
            temp_sum[j] += isCarry;
            isCarry = 0;
        }
        
        if( temp_sum[j] >= 10) {
            isCarry = temp_sum[j] / 10;
            temp_sum[j] %= 10;
        }
    }
    
    
    // 打印二维数组
    for (int i = 0; i < num2; i++) { // 行
        for (int j = 0; j < num1 + num2; j++)  // 列
            printf("%d  ", temp[i][j]);
        printf("\n");
    }
    printf("\n");
    
    
    // 打印相乘结果
    for(int i = 0; i < num2 + num1; i++)
        printf("%d", temp_sum[i]);
}

int main()
{
    //int a1[num1] = { 2, 3, 4 };
    //int a2[num2] = { 4, 5, 6 };
    // 2739236198310827136103974、37261038163103818366
    int a1[num1] = { 2, 7, 3, 9, 2, 3, 6, 1, 9, 8, 3 };
    int a2[num2] = { 3, 7, 2, 6, 1, 0 };
    
    MultiOfLargeNumbers(a1, a2);
    return 0;
}

0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  
0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  
0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  
0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  
0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  
0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
  
0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  
0  0  0  0  0  2  7  3  9  2  3  6  1  9  8  3  0  
0  0  0  1  6  4  3  5  4  1  7  1  8  9  8  0  0  
0  0  0  5  4  7  8  4  7  2  3  9  6  6  0  0  0  
0  1  9  1  7  4  6  5  3  3  8  8  1  0  0  0  0  
0  8  2  1  7  7  0  8  5  9  4  9  0  0  0  0  0 
 
10206667998485630

四、分治法

首先将 X 分为 A、B 和 Y 分成 C、D。注意:这里的 X、Y 假设位数相同。


此时将 X 和 Y 的乘积转化,把问题转化为求解式子的值。


分析一下:对这个式子,一共要进行 4 次 n/2 的乘法(AC 2 次, AD、BC 各 1 次)和 3 次加法,因而该算法的时间复杂度为

T(n) = 4 * T(n/2) + θ(n)

通过 master 定理可以求得  T(n) = θ(n2),跟小学算法的时间复杂度没有区别。

但是我们再来看看,我们是否可以用加法来换取乘法?因为多一个加法操作,也是常数项,对时间复杂度没有影响,如果减少一个乘法则不同。


时间复杂度为:

T(n) = 3 * T(n/2) + θ(n),通过 master 定理求得 T(n) = O(nlog23) = O(n1.59)。

五、内容来源

分治算法详解
分治法的经典问题——大整数相乘

posted @ 2020-03-15 13:27  和风细羽  阅读(197)  评论(0编辑  收藏  举报