一、问题描述
计算两个很大整数的结果,例如: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 次加法,因而该算法的时间复杂度为
通过 master 定理可以求得 T(n) = θ(n2),跟小学算法的时间复杂度没有区别。
但是我们再来看看,我们是否可以用加法来换取乘法?因为多一个加法操作,也是常数项,对时间复杂度没有影响,如果减少一个乘法则不同。
时间复杂度为:
T(n) = 3 * T(n/2) + θ(n),通过 master 定理求得 T(n) = O(nlog23) = O(n1.59)。