看了就会的大整数乘法运算与分治算法

开发 前端 算法
在数据加密处理中有很多复杂的加密算法,这些加密算法往往会用到很多超大的整数运算。

[[352004]]

在数据加密处理中有很多复杂的加密算法,这些加密算法往往会用到很多超大的整数运算。不过,程序设计语言对数据的大小会有一定的限制,数据太大就会出现数据溢出的情况,这是无法进行大整型数据运算的。本文将和大家一起学习如何实现大整数的数据运算,本文代码我们使用C++实现。

普通乘数运算

对于乘数运算有一种比较简单较为容易理解的方法,我们可以利用小学时期学的列竖式的计算方法进行乘法运算。

列竖式乘法运算

参考上图中的列竖式计算方法,我们进行代码实现。

  1. #include <iostream> 
  2. #include <string> 
  3. #include <stdlib.h> 
  4. #include <vector> 
  5. #include <cstring> 
  6. #include <algorithm> 
  7.  
  8. std::string multiply(std::string a, std::string b) 
  9.     std::string result = ""
  10.     int row = b.size(); 
  11.     int col = a.size() + 1; 
  12.     int tmp[row][col]; 
  13.     memset(tmp,0, sizeof(int)*row*col); 
  14.      
  15.     reverse(a.begin(),a.end()); 
  16.     reverse(b.begin(),b.end()); 
  17.      
  18.     for(int i = 0; i < b.size(); i++) 
  19.     { 
  20.         for(int j = 0; j < a.size(); j++) 
  21.         { 
  22.             std::string bit_a = std::string(1, a.at(j)); 
  23.             std::string bit_b = std::string(1, b.at(i)); 
  24.              
  25.             tmp[i][j] += std::stoi(bit_a) * std::stoi(bit_b); 
  26.          
  27.             tmp[i][j+1] = tmp[i][j] / 10; 
  28.             tmp[i][j] %= 10; 
  29.  
  30.         } 
  31.  
  32.     } 
  33.  
  34.     int N =  a.size() + b.size(); 
  35.     int sum[N]; 
  36.     memset(sum, 0, sizeof(int)*N); 
  37.      
  38.     for(int n = 0; n < N; n++) 
  39.     { 
  40.         int i = 0; 
  41.         int j = n; 
  42.          
  43.         while (i <= n && j >= 0 ) 
  44.         { 
  45.             if(i < row && j < col) 
  46.             { 
  47.                 sum[n] += tmp[i][j]; 
  48.             } 
  49.              
  50.             i++; 
  51.             j--; 
  52.         } 
  53.  
  54.         if( n+1 < N ) 
  55.         { 
  56.             sum[n+1] = sum[n] / 10; 
  57.             sum[n] %= 10; 
  58.         } 
  59.  
  60.     } 
  61.  
  62.     bool zeroStartFlag = true
  63.     for (int i = N-1; i >= 0; i--) 
  64.     { 
  65.         if(sum[i]==0 && zeroStartFlag) 
  66.         { 
  67.             continue
  68.         } 
  69.          
  70.         zeroStartFlag = false
  71.         result.append(std::to_string(sum[i])); 
  72.     } 
  73.      
  74.     return result; 
  75.  
  76.  
  77. int main() 
  78.     std::string a = "3456"
  79.     std::string b = "1234"
  80.  
  81.     std::string result = multiply(a, b);     
  82.     std::cout << a << " * " << b << " = " << result <<std::endl; 
  83.      
  84.     return 0; 

为了方便我们先将各个乘数做了翻转处理,最后需要再将结果翻转回来。在运算过程中用来存放乘法运算结果的数组,我们没有进行移位处理同列相加,而是对角线相加,这样可以减少空间和运算步骤。上面的代码运行结果如下所示。

运行结果

因为字符串的长度没有特别的限制,所以上面的算法可以适用大整数运算。

分治算法

虽然上面的列竖式的方法可以很好的解决大整数乘法的问题,但是我们还用一种更加高效的方法可以选择,这就是分治(Divide and Conquer)算法。它是一种非常重要的算法,可以应用到很多领域,比如快速排序,二分搜索等。从算法的名字我们可以看出它是将大的问题拆分进行细化,由大变小,先将小的问题解决,最终将这个问题解决,所以叫Divide and Conquer。在这里我们可以通过这种方法将大整数进行拆分,拆分成一个个可以通过程序语言直接进行运算的小整进行计算,最后求得到大整数的值。

假设有两个大整数,我们设为a(大小为n位)和b(大小为m位),这里我们将使用二分法对数据进行拆分,这两个整数我们可以分解为:

则,

令,

根据上面公式里,我们可以将a*b分解为四个小的整数的乘法,即z3,z2,z1,z0四个表达式。如果分解出来的乘法数值还是很大,还可以按照同样的方法进行拆解直到拆解成较小的数值乘法,直到计算机程序语言可以直接运算。

比如,上面的3456和1234相乘,参考下图通过二分法逐级对整数进行拆分,我们将两个整数拆分到一位整数进行运算。

3456和1234拆分步骤图

下面我们看一下分治算法的代码实现,这里我们使用递归的方法。

  1. #include <iostream> 
  2. #include <string> 
  3. #include <stdlib.h> 
  4. #include <vector> 
  5. #include <cstring> 
  6. #include <algorithm> 
  7. #include <cmath> 
  8.  
  9. std::string add(std::string a, std::string b) 
  10.     int N = std::max(a.size(), b.size()); 
  11.     int sum[N]; 
  12.     memset(sum, 0, sizeof(int)*N); 
  13.      
  14.     reverse(a.begin(),a.end()); 
  15.     reverse(b.begin(),b.end()); 
  16.  
  17.     for(int i = 0; i< N; i++) 
  18.     { 
  19.         int bit_a = 0; 
  20.         int bit_b = 0; 
  21.         if(i < a.size()) bit_a = std::stoi(std::string(1, a.at(i))); 
  22.         if(i < b.size()) bit_b = std::stoi(std::string(1, b.at(i))); 
  23.  
  24.         sum[i] += (bit_a + bit_b); 
  25.  
  26.         if(i < N-1 && sum[i]>9) 
  27.         { 
  28.             sum[i+1] = sum[i] / 10; 
  29.             sum[i] %=10; 
  30.         } 
  31.     } 
  32.  
  33.     std::string result=""
  34.     bool zeroStartFlag = true
  35.     for (int i = N-1; i >= 0; i--) 
  36.     { 
  37.         if(sum[i]==0 && zeroStartFlag) 
  38.         { 
  39.             continue
  40.         } 
  41.          
  42.         zeroStartFlag = false
  43.         result.append(std::to_string(sum[i])); 
  44.     } 
  45.  
  46.  
  47.     return result; 
  48.  
  49. std::string divideAndConquer(std::string a, std::string b) 
  50.     if( a.size() < 2 && b.size() < 2)  
  51.     { 
  52.         return std::to_string(std::stoi(a) * std::stoi(b)); 
  53.     } 
  54.      
  55.     int n = a.size(); 
  56.     int m = b.size(); 
  57.      
  58.     int halfN = n/2; 
  59.     int halfM = m/2; 
  60.  
  61.     std::string a0 = "0"
  62.     std::string a1 = "0"
  63.     if(a.size() > halfN && halfN > 0) 
  64.     { 
  65.         a1 = a.substr(0, halfN); 
  66.         a0 = a.substr(halfN, a.size() - halfN); 
  67.     } 
  68.     else 
  69.     { 
  70.         a1 = "0"
  71.         a0 = a; 
  72.     } 
  73.      
  74.     std::string b0 = "0"
  75.     std::string b1 = "0"
  76.     if(b.size() > halfM && halfM > 0) 
  77.     { 
  78.         b1 = b.substr(0, halfM); 
  79.         b0 = b.substr(halfM, b.size() - halfM); 
  80.  
  81.     } 
  82.     else 
  83.     { 
  84.         b1 = "0"
  85.         b0 = b; 
  86.     } 
  87.  
  88.     std::string a1b1 = divideAndConquer(a1, b1); 
  89.     std::string a0b0 = divideAndConquer(a0, b0); 
  90.     std::string a1b0 = divideAndConquer(a1, b0); 
  91.     std::string a0b1 = divideAndConquer(a0, b1); 
  92.      
  93.     a1b1.append((n - halfN) + (m - halfM), '0'); 
  94.     a1b0.append(n - halfN, '0'); 
  95.     a0b1.append(m - halfM, '0'); 
  96.  
  97.     std::string result = ""
  98.     result = add(a1b1, a1b0); 
  99.     result = add(result, a0b1); 
  100.     result = add(result, a0b0); 
  101.  
  102.     return result; 
  103.  
  104. int main() 
  105.     std::string a = "3456"
  106.     std::string b = "1234"
  107.  
  108.     std::cout << a << " * " << b << " = " << divideAndConquer(a, b) << std::endl;  
  109.  
  110.     return 0; 

程序的运行结果如下:

分治算法运行结果

本文转载自微信公众号「Will的大食堂」,可以通过以下二维码关注。转载本文请联系Will的大食堂公众号。

 

责任编辑:武晓燕 来源: Will的大食堂
相关推荐

2022-07-12 08:27:18

Zadig开源

2021-04-19 11:40:15

浏览器路径

2021-05-12 09:07:09

Java数据结构算法

2020-06-05 18:09:14

TomcatWeb应用服务器

2023-01-08 23:06:14

css3d变换

2020-10-20 08:14:08

算法与数据结构

2020-12-02 09:36:20

算法分支思想

2021-03-24 15:10:11

算法科学技术

2019-05-28 14:33:07

Javascript运算符前端

2020-11-09 10:01:29

Python乘法位运算

2024-03-11 12:21:07

模型数据

2021-07-16 10:46:52

PythonNumpy数据溢出

2017-09-18 09:26:51

PHP代码大整数

2011-06-08 17:45:54

AOFAX传真机

2020-08-12 07:59:15

Long类型

2024-10-22 15:41:47

NumPyPython

2020-09-15 12:40:16

回溯算法代码回溯法

2022-08-28 20:50:29

算法模型机器学习
点赞
收藏

51CTO技术栈公众号