01. 问题描述
编写一个能计算两个n(16<=n<=256)位整数的乘法。
02. 输入格式
输入两行数据,每行表示一个大整数
03. 输出格式
输出运算后大整数的数据值
04. 输入样例
1111111111111111
1111111111111111
05. 输出样例
结果:1234567901234567654320987654321
06. 问题分析
- 大整数的位数n≥16,可以是十进制或者二进制数。
- 两个大整数相乘如果按照传统的方法,需要耗费n2次数量相乘,利用分治算法,可降低其时间复杂度。
- 分治实现思想:将第一个整数分成AB两部分,第二个整数分成CD两部分,当n=1时,直接利用普通乘法相乘,当n>1时,根据公式分治解乘法AC、(B-A)(C-D)、BD,然后根据公式将AC、(B-A)(C-D)、BD相加并移动n/2位,将AC结果移动n位,最后合并求解结果。
- 这样,原整数乘法化为3次n/2规模整数乘法运算和6次加减运算(Θ(n))
公式一:
AC*10^2^ + ( B-A)(C-D) + AC + BD )*10^n/2^ + BD
公式二:AC*10^n^ + ( A+B)(C+D) - AC - BD )*10^n/2^ + BD
- 以下实现采用第一个公式实现
07. 算法设计
算法设计:
算法:MULTIFY
input:存储数组x,y;整数的位数n
output:相乘结果数组result
过程:Multify(x,y,result,n)
1. if n=1 直接相乘计算结果
2. else
3. x_div2[1…n/2] ← x[1…n/2]
4. x_div1[1…n/2] ← x[n/2+1…n]
5. y_div2[1…n/2] ← y[1…n/2]
6. y_div1[1…n/2] ← y[n/2+1…n]
7. Multify(x_div1,y_div2,ac,n/2)
8. Multify(x_div2,y_div2,bd,n/2)
9. subx[1…n/2] ← x_div2[1…n/2] – x_div1[1…n/2]
10. suby[1…n/2] ← y_div1[1…n/2] – y_div2[1…n/2]
11. Multify(subx,suby,middleresult_1,n/2)
12. addxy_1 ← ac + bd;
13. middleresult_2 ← addxy_1 + middleresult_1
14. move middleresult_2 n/2位
15. move ac n位
16. addxy_2 ← ac + bd;
17. result ← addxy_2 + middleresult_2
18. endif
08. 代码实现
位数对齐函数:
/*位数对齐函数
*将输入的字符串转换为数字数组(数字数组倒序存储,即高位放在后面,并在数组[0]位置放置数字位的位数)
*位数对齐:为了方便计算,直接对齐到一个大于两数最大位数的一个2的幂次
*/
int align(char* ch_x,char* ch_y,int& ch_x_length,int& ch_y_length){
int k = 0;
int n = pow(2,k);
while(1){
if(n>=max(ch_x_length,ch_y_length))
break;
++k;
n = pow(2,k);
}
int i = 0,j = 0;
if(ch_x_length < n){
char ch_x_tmp[n];
for(;i<n;++i){
if(i < n - ch_x_length)
ch_x_tmp[i] = '0';
else
ch_x_tmp[i] = ch_x[j++];
}
strncpy(ch_x,ch_x_tmp,n);
ch_x_length = n;
}
i = 0;
j = 0;
if(ch_y_length < n){
char ch_y_tmp[n];
for(;i<n;++i){
if(i < n - ch_y_length)
ch_y_tmp[i] = '0';
else
ch_y_tmp[i] = ch_y[j++];
}
strncpy(ch_y,ch_y_tmp,n);
ch_y_length = n;
}
return n;
}
大整数加法函数:
/*大整数加法函数
*考虑进位
*并将两数相加和直接补位到指定new_bits位数,存于addxy数组中
*/
void Add(int* x,int* y,int* addxy,int new_bits){//这里应当保证位数的对齐
for(int i = 1;x[0]+i <= new_bits;++i)
x[x[0]+i] = 0;
for(int i = 1;y[0]+i <= new_bits;++i)
y[y[0]+i] = 0;
x[0] = new_bits;
y[0] = new_bits;
int carry = 0;//进位位
for(int i = 1;i<=new_bits;++i){
addxy[i] = (x[i]+y[i]+carry)%10;
if(x[i]+y[i]+carry>9)
carry = 1;
else
carry = 0;
}
if(carry == 1){
addxy[0] = new_bits+1;//结果长度
addxy[addxy[0]] = 1;//进位为1
}
else
addxy[0] = new_bits;//结果长度
}
大整数减法函数:
/*大整数减法函数
*减法位数不会改变
*考虑大减小和小减大两种情况
*用数组[0]位的符号表示结果的正负,绝对值为其位数
*/
void Sub(int* x,int* y,int* subxy,int bits){ //x-y 减位 位数不变
int flag = 0;
for(int i = bits;i>=1;--i){
if((x[i]!=0||y[i]!=0)&&(x[i]!=y[i])){
if(x[i]<y[i])
flag = 1;
break;
}
}
if(flag == 0){ //大减小
int borrow = 0;//借位
for(int i = 1;i <= bits;++i){
if(x[i]-y[i]<0){
x[i+1]--;
borrow = 1;
}
else
borrow = 0;
subxy[i] = (10*(borrow) + x[i] - y[i]);
}
subxy[0] = bits;
}
else{ //小减大
int borrow = 0;//借位
for(int i = 1;i <= bits;++i){
if(y[i]-x[i]<0){
y[i+1]--;
borrow = 1;
}
else
borrow = 0;
subxy[i] = (10*(borrow) + y[i] - x[i]);
}
subxy[0] = -bits;
}
}
位数移动函数:
/*位数移动函数
*即扩大10的n次幂
*这里考虑的是倒序存储的情况
*/
void Move(int* arr,int n){ //正序左移n位 //这里先扩大位数再移位
for(int i=arr[0];i>=1;--i)
arr[i+n]= arr[i];
for(int i = 1;i<=n;++i)
arr[i] = 0;
arr[0] += n;
}
分治函数:
/*分治函数
*将一个大整数分成左右两部分
*/
void Divide(int* arr_1,int* arr_2,int s,int t){ //由arr_1分到arr_2
int i;
for(i = 1;i <= t - s;++i)
arr_2[i] = arr_1[s+i-1];
arr_2[0] = t -s;
}
大整数乘法函数:
/*大整数乘法实现函数
*/
void Multify(int* x,int* y,int* result,const int& n){ //大整数乘法
//申请空间存放数据四部分
int* x_div1 = (int *)malloc((n*8+1)*sizeof(int));
int* x_div2 = (int *)malloc((n*8+1)*sizeof(int));
int* y_div1 = (int *)malloc((n*8+1)*sizeof(int));
int* y_div2 = (int *)malloc((n*8+1)*sizeof(int));
//递归出口
if(n==1){
result[1]=x[1]*y[1];
if(result[1]<=9)
result[0] = 1;
else{//存在进位
result[2]=result[1]/10;
result[1]%=10;
result[0] = 2;
}
if(x[0]*y[0]<0){ //判断正负
result[0] = - abs(result[0]);
}
}
else{
//分成四部分
Divide(x,x_div2,1,(n>>1)+1);
Divide(x,x_div1,n/2+1,n+1);
Divide(y,y_div2,1,(n/2)+1);
Divide(y,y_div1,n/2+1,n+1);
int* ac = (int *)malloc((n*32+1)*sizeof(int));//分治结果
int* bd = (int *)malloc((n*32+1)*sizeof(int));//分治结果
int* addxy_1 = (int *)malloc((n*32+1)*sizeof(int));//存放加法和
int* addxy_2 = (int *)malloc((n*32+1)*sizeof(int));//存放加法和
int* subx = (int *)malloc((n*32+1)*sizeof(int));//存放减法差
int* suby = (int *)malloc((n*32+1)*sizeof(int));//存放减法差
int* middle_result_1 = (int *)malloc((n*32+1)*sizeof(int));//中间结果
int* middle_result_2 = (int *)malloc((n*32+1)*sizeof(int));//中间结果
Multify(x_div1,y_div1,ac,n/2);//分治
Multify(x_div2,y_div2,bd,n/2);//分治
Sub(x_div2,x_div1,subx,n/2); //B-A
Sub(y_div1,y_div2,suby,n/2); //C-D
Multify(subx,suby,middle_result_1,n/2);//分治
Add(ac,bd,addxy_1,n*2);
if(middle_result_1[0] < 0){ //根据正负号 判断接下来的某种操作
for(int i = 1;abs(middle_result_1[0])+i <= addxy_1[0];++i)
middle_result_1[middle_result_1[0]+i] = 0;
Sub(addxy_1,middle_result_1,middle_result_2,n*2);
}
else
Add(addxy_1,middle_result_1,middle_result_2,n*4);
//下面将所用到的进行退位,排除没用的0空位 因为当前的值的每个位数可能差距太大
for(int i = abs(middle_result_2[0]);i>=1;--i){
if(middle_result_2[i]==0)
continue;
else{
middle_result_2[0] = i;
break;
}
}
for(int i = abs(ac[0]);i>=1;--i){
if(ac[i]==0)
continue;
else{
ac[0] = i;
break;
}
}
for(int i = abs(bd[0]);i>=1;--i){
if(bd[i]==0)
continue;
else{
bd[0] = i;
break;
}
}
//合并
Move(middle_result_2,n/2);
Move(ac,n);
Add(ac,bd,addxy_2,n*2);
Add(addxy_2,middle_result_2,result,n*2);
if(x[0]*y[0]<0) //判断符号
result[0] = -abs(result[0]);
else
result[0] = abs(result[0]);
}
}
主函数:
/*main函数
*/
#include <iostream>
using namespace std;
#include <bits/stdc++.h>
#include <math.h>
#include <cstdlib>
#include <stdio.h>
#include <cstring>
int main(){
const int maxbits = 1024;
char ch_x[maxbits];
char ch_y[maxbits];
scanf("%s",&ch_x);
scanf("%s",&ch_y);
int ch_x_length = strlen(ch_x);
int ch_y_length = strlen(ch_y);
int bits = align(ch_x,ch_y,ch_x_length,ch_y_length);//补位
int* x = (int *)malloc((maxbits*2+1)*sizeof(int));
int* y = (int *)malloc((maxbits*2+1)*sizeof(int));
int* addxy = (int *)malloc((maxbits*4+1)*sizeof(int));
int* subxy = (int *)malloc((maxbits*4+1)*sizeof(int));
int* result = (int *)malloc((maxbits*8+1)*sizeof(int));
x[0] = bits;//默认输入的都是正整数
y[0] = bits;//默认输入的都是正整数
for(int i = 0;i<bits;++i){ //逆序存储 方便进位
x[bits-i] = ch_x[i] - '0';
y[bits-i] = ch_y[i] - '0';
}
Multify(x,y,result,bits); //调用函数
//打印实现
cout<<"结果:";
int flag = 0;
for(int i = abs(result[0]);i>=1;--i){
if(result[i]!=0){
flag = i;
break;
}
}
for(int i = flag;i>=1;--i)
cout<<result[i];
return 0;
}
09. 测试结果
1234567890123
1234567890123
结果:1524157875322755800955129
Process returned 0 (0x0) execution time : 52.699 s
Press any key to continue.
10. 复杂度分析
——————END-2022-04-26——————