zoukankan      html  css  js  c++  java
  • 大整数相乘

    一、问题描述

    计算两个很大整数的结果,例如: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("
    ");
        }
        printf("
    ");
        
        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("
    ");
        }
        printf("
    ");
        
        
        // 打印相乘结果
        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)。

    五、内容来源

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

  • 相关阅读:
    形态学权重图像去噪
    druid配置
    @mapper个人理解
    mybatis自动生成的bean接口在service层找不到
    type-aliases-package的作用
    windows查询端口是否被占用
    idea中使用lombok注解无效
    springboot+dubbo问题记录
    springboot跳转到其他controller
    BigDecimal做减法计算
  • 原文地址:https://www.cnblogs.com/dins/p/multiplication-of-large-integers.html
Copyright © 2011-2022 走看看