大整数乘法

    科技2022-09-05  118

    通常印象中的乘法:

    位数增加时,许多编程语言中,往往连long_int类型都无法存储一定程度上的大整数

    根据乘法算式的思想,将大整数分块计算,每个子运算都是一位数乘一位数,通过数组的形式输出到屏幕,可以做到所谓的大整数乘法

    #include <iostream> #include <cstring> using namespace std; const int MAX_Length = 1000; void long_multiplication(int* res, int* num1, int* num2); void long_multiplication(int* res, int* num1, int* num2) { //num1[0]和num2[0]存储位数,这里res[0]取其加和,因为2位数乘2位数,最多4位数,99*99=9801 res[0] = num1[0] + num2[0]; //逐位相乘,想不明白搞个11*11,看一下121中的2是怎么来的 for (int i = 1; i <= num1[0]; i++) { for (int j = 1; j <= num2[0]; j++) { res[i + j] += num1[i] * num2[j]; } } //因为有res中有2位数,所以处理进位 for (int x = 1; x <= res[0]; x++) { if (res[x] < 10) { //小于10没有事了,让它跳出当前循环,继续走判断下一个res continue; } else { //让它的前一位加进位 res[x + 1] += res[x] / 10; //自身去模10 res[x] = res[x] % 10; //循环次数一定要等于大整数结果的长度,res[0]还要自加 x == res[0] && res[0]++; } } } int main(int argc, char** argv) { char str1[MAX_Length], str2[MAX_Length]; int num1[MAX_Length], num2[MAX_Length], res[MAX_Length] = { 0 }; //注意res数组需要初始化,否则结果可能不正确 cin >> str1; cout << "imput the 2" << endl; cin >> str2; //获取num1大数长度 num1[0] = strlen(str1); //倒着写入num1,2的数组 for (int i = 0; str1[i]; i++) { num1[num1[0] - i] = str1[i] - '0'; } //获取num1大数长度 num2[0] = strlen(str2); for (int i = 0; str2[i]; i++) { num2[num2[0] - i] = str2[i] - '0'; } long_multiplication(res, num1, num2); for (int i = res[0]; i >=2; i--) { //第0位用来保存长度,第一位并没有使用 std::cout << res[i]; } cout << endl; return 0; }

    这种算法的时间复杂度是n^2量级的,有没有一种方法能在保证解决大整数乘法情况下,适当降低时间复杂度

    根据分治法的思想,将一个大问题,转换为多个子问题,然后将比较容易操作的子问题得出结果,再次合并,得到原问题的结果

    对于大整数乘法,我们同样采取这样的思想

    当子问题中的算式分裂到个位数乘个位数,求和合并,这实际上与分块算式乘法很类似,实际上根据分治的思想,每次都是将大整数一分为二,每一部分乘属于它的数量级。

    A*B = (A1+A2)*(B1+B2) = (A11+A12)*(A21+A22)*(B11+B12)*(B21+B22)=.........

    根据该逻辑,推测时间复杂度为log2N的数量级

    为了代码和思路的简洁性,采用递归程序

    #include <cstdio> #include <cmath> /*递归思想 * 对X = 1234 和 Y = 5678来分析: divideConquer(1234, 5678, 4) ———————————————————————— X=1234 | A=12 | B=34 | A-B=-22 Y=5678 | C=56 | D=78 | D-C=22 三次递归: AC = divideConquer(12, 56, 2) BD = divideConquer(34, 78, 2) (A-B)(D-C) = divideConquer(-22, 22, 2) AC递归 ———————————————————————— X=12 | A1=1 | B1=2 | A1-B1=-1 Y=56 | C1=5 | D1=6 | D1-C1=1 A1*C1 = divideConquer(1, 5, 1) = 5 B1*D1 = divideConquer(2, 6, 1) = 12 (A1-B1) * (D1-C1) = divideConquer(-1, 1, 1) = -1; 所以AC递归求得的值为:5 * 10 ^ 2 + (-1 + 5 + 12)* 10 + 12 = 672 同理可得 BD = 2652 和 (A-B)(D-C) = -484 最终可得 X * Y = 7006652 */ using namespace std; int long_multiplication(int* x, int* y, int len) { if (*x == 0 || *y == 0) { return 0; } else if(len == 1){ return(*x) * (*y); } else { int A_F = (int)*x / pow(10, (int)len / 2);//A大整数的第一部分 int A_S = *x - A_F * pow(10,(int)len /2);//A大整数的第二部分(分界点n/2) int B_F = (int)*y / pow(10, (int)len / 2);//B大整数的第一部分 int B_S = *y - B_F * pow(10, (int)len / 2);//B大整数的第二部分 int A_FmulB_F = long_multiplication(&A_F, &B_F, len / 2);//第一部分与第一部分相乘 int A_SmulB_S = long_multiplication(&A_S, &B_S, len / 2);//第二部分与第二部分相乘 //A1-B1 int temp1 = A_F - A_S; //B2-B1 int temp2 = B_S - B_F; int ALL_AB = long_multiplication(&temp1, &temp2, len / 2) + A_FmulB_F + A_SmulB_S; //AC * pow(10 , n) + ABDC * pow(10, (int)(n / 2)) + BD return A_FmulB_F * pow(10, len) + ALL_AB * pow(10, (int)(len / 2)) + A_SmulB_S; } } int main(int argc, char** argv[]) { int A; int B; A = 1234; B = 5678; printf("%d", long_multiplication(&A, &B, 4)); }

     

    Processed: 0.008, SQL: 12