大整数乘法
问题描述
通常,在分析一个算法的计算复杂性时,都将加法和乘法运算当作是基本运算来处理,即将执行一次加法或乘法运算所需的计算时间当作一个仅取决于计算机硬件处理速度的常数。
这个假定仅在计算机硬件能对参加运算的整数直接表示和处理时才是合理的。然而,在某些情况下,我们要处理很大的整数,它无法在计算机硬件能直接表示的范围内进行处理。若用浮点数来表示它,则只能近似地表示它的大小,计算结果中的有效数字也受到限制。若要精确地表示大整数并在计算结果中要求精确地得到所有位数上的数字,就必须用软件的方法来实现大整数的算术运算。
请设计一个有效的算法,可以进行两个n位大整数的乘法运算。
参考解答
设X和Y都是n位的二进制整数,现在要计算它们的乘积XY。我们可以用小学所学的方法来设计一个计算乘积XY的算法,但是这样做计算步骤太多,显得效率较低。如果将每2个1位数的乘法或加法看作一步运算,那么这种方法要作O(n2)步运算才能求出乘积XY。下面我们用分治法来设计一个更有效的大整数乘积算法。
图6-3 大整数X和Y的分段
我们将n位的二进制整数X和Y各分为2段,每段的长为n/2位(为简单起见,假设n是2的幂),如图6-3所示。
由此,X=A2n/2+B ,Y=C2n/2+D。这样,X和Y的乘积为:
XY=(A2n/2+B)(C2n/2+D)=AC2n+(AD+CB)2n/2+BD (1)
如果按式(1)计算XY,则我们必须进行4次n/2位整数的乘法(AC,AD,BC和BD),
以及3次不超过n位的整数加法(分别对应于式(1)中的加号),此外还要做2次移位(分别对应于式(1)中乘2n和乘2n/2)。所有这些加法和移位共用O(n)步运算。设T(n)是2个n位整数相乘所需的运算总数,则由式(1),我们有:
(2)
由此可得T(n)=O(n2)。因此,用(1)式来计算X和Y的乘积并不比小学生的方法更有效。要想改进算法的计算复杂性,必须减少乘法次数。为此我们把XY写成另一种形式:
XY=AC2n+[(A-B)(D-C)+AC+BD]2n/2+BD (3)
虽然,式(3)看起来比式(1)复杂些,但它仅需做3次n/2位整数的乘法(AC,BD和(A-B)(D-C)),6次加、减法和2次移位。由此可得:
(4)
用解递归方程的套用公式法马上可得其解为T(n)=O(nlog3)=O(n1.59)。利用式(3),并考虑到X和Y的符号对结果的影响,我们给出大整数相乘的完整算法MULT如下:
function MULT(X,Y,n); {X和Y为2个小于2n的整数,返回结果为X和Y的乘积XY}
begin
S:=SIGN(X)*SIGN(Y); {S为X和Y的符号乘积}
X:=ABS(X);
Y:=ABS(Y); {X和Y分别取绝对值}
if n=1 then
if (X=1)and(Y=1) then return(S)
else return(0)
else begin
A:=X的左边n/2位;
B:=X的右边n/2位;
C:=Y的左边n/2位;
D:=Y的右边n/2位;
ml:=MULT(A,C,n/2);
m2:=MULT(A-B,D-C,n/2);
m3:=MULT(B,D,n/2);
S:=S*(m1*2n+(m1+m2+m3)*2n/2+m3);
return(S);
end;
end;
#include <iostream> using namespace std; #define MAX 64 struct Number { char num[MAX];//结果 int size; //长度 char sign; //符号 }; //输入函数,初始化num全部为0,不足n位的补齐 void Input(Number &number1,Number &number2) { char num1[MAX], num2[MAX]; int a, b, size, i; cin>>num1>>num2; a = strlen(num1); b = strlen(num2); if (num1[0] == '-') a=a-1; if (num2[0] == '-') b=b-1; size = (a > b) ? a: b; number1.size = size; number2.size = size; if (num1[0] == '-') { number1.sign = '-'; if(a>b) { for ( i=1; i<a+1;i++) number1.num[MAX-size+i-1] = num1[i]; } else { for ( i=1; i<a+1;i++) number1.num[MAX-size+i-1+b-a] = num1[i]; } } else { number1.sign = '+'; if(a>b) { for ( i=0; i<a;i++) number1.num[MAX-size+i] = num1[i]; } else { for ( i=0; i<a;i++) number1.num[MAX-size+i+b-a] = num1[i]; } } if (num2[0] == '-') { number2.sign = '-'; if (a<b) { for ( i=1; i<b+1;i++) number2.num[MAX-size+i-1] = num2[i]; } else { for ( i=1; i<b+1;i++) number2.num[MAX-size+i-1+a-b] = num2[i]; } } else { number2.sign = '+'; if(a<b) { for ( i=0; i<b;i++) number2.num[MAX-size+i] = num2[i]; } else { for ( i=0; i<b;i++) number2.num[MAX-size+i+a-b] = num2[i]; } } } //计算10的n次方 Number Order(int n) { Number num; num.size = n+1; num.sign = '+'; int i; num.num[MAX-num.size]='1'; for (i=1; i<num.size; i++) num.num[MAX-num.size+i]='0'; return num; } //减法 Number Sub(Number number1, Number number2) { Number num,num1; int i,a,b,m,size; for (i=0; i<MAX; i++) num.num[i]='0'; a=number1.size; b=number2.size; size = (a > b) ? a: b; bool flag = false; num.sign='+'; num.size=size; for (i=0; i<size; i++) { if(number1.num[MAX-size+i]!=number2.num[MAX-size+i]) { if (number1.num[MAX-size+i]<number2.num[MAX-size+i]) { flag = true; } break; } } if (flag) { num.sign='-'; num1=number1; number1=number2; number2=num1; } for(i=0; i<size; i++) { m=0; if(number1.num[MAX-1-i]-number2.num[MAX-1-i]<0) { m=10; number1.num[MAX-2-i]=number1.num[MAX-2-i]-1; } num.num[MAX-1-i]=number1.num[MAX-1-i]-number2.num[MAX-1-i]+48+m; } return num; } //加法 Number ToAdd(Number number1, Number number2) { Number num; int i,a,b,m,size; for (i=0; i<MAX; i++) num.num[i]='0'; a=number1.size; b=number2.size; size = (a > b) ? a: b; if (number1.sign-number2.sign!=0) { if(number1.sign=='+'&&number2.sign=='-') num = Sub(number1,number2); if(number1.sign=='-'&&number2.sign=='+') num = Sub(number2,number1); return num; } for(i=0; i<size; i++) { m=0; if(number1.num[MAX-1-i]+number2.num[MAX-1-i]-96>=10) { if(i==size-1) num.num[MAX-2-i]='1'; m=10; number1.num[MAX-2-i]=number1.num[MAX-2-i]+1; } num.num[MAX-1-i]=number1.num[MAX-1-i]+number2.num[MAX-1-i]-48-m; } if(num.num[MAX-size-1]!='0') num.size=size+1; else num.size=size; if (number1.sign=='+'&&number2.sign=='+') num.sign='+'; if (number1.sign=='-'&&number2.sign=='-') num.sign='-'; return num; } Number Add(Number number1, Number number2, Number number3) { Number num1,num; num1=ToAdd(number1,number2); num=ToAdd(num1,number3); return num; } //求数组的左半部分 Number HalfLeft(Number number) { Number num; int i; for (i=0; i<MAX; i++) num.num[i]='0'; for (i=0; i<number.size/2; i++) num.num[MAX-number.size/2+i]=number.num[MAX-number.size+i]; num.sign = '+'; num.size = number.size/2; return num; } //求数组的右半部分 Number HalfRight(Number number) { Number num; int i; for (i=0; i<MAX; i++) num.num[i]='0'; for (i=0; i<number.size/2; i++) num.num[MAX-number.size/2+i]=number.num[MAX-number.size/2+i]; num.sign = '+'; num.size = number.size/2; return num; } //乘法 Number Single1Mult(Number number1, Number number2) { Number num; int a,i; char str[3]; for (i=0; i<MAX; i++) num.num[i]='0'; a = (number1.num[MAX-1]-48)*(number2.num[MAX-1]-48); itoa (a,str,10); num.size = strlen(str); for(i=0; i<num.size; i++) num.num[MAX-num.size+i]=str[i]; return num; } Number Single2Mult(Number number1, Number number2) { Number num; num.size = number1.size+number2.size-1; num.sign=number1.sign; int i; for (i=0; i<MAX; i++) num.num[i]='0'; for (i=0; i<number1.size; i++) num.num[MAX-num.size+i]=number1.num[MAX-number1.size+i]; return num; } //结果计算 Number Result(Number M1, Number M2, Number M3, int n) { Number result, p1, p2, p3; p1 = Single2Mult(M1,Order(n)); p2 = Add(M1,M2,M3); p3 = Single2Mult(p2,Order(n/2)); result = Add(p1,p3,M3); return result; } //分治法求解 Number Mult(Number number1, Number number2, int n) { Number result, A, B, C, D, M1, M2, M3; if(n==1) { result = Single1Mult(number1,number2); if(number1.sign-number2.sign==0) result.sign='+'; else result.sign='-'; return result; } else { A = HalfLeft(number1); B = HalfRight(number1); C = HalfLeft(number2); D = HalfRight(number2); M1 = Mult(A,C,n/2); M2 = Mult(Sub(A,B),Sub(D,C),n/2); M3 = Mult(B,D,n/2); result = Result(M1,M2,M3,n); if(number1.sign-number2.sign==0) result.sign='+'; else result.sign='-'; return result; } } //结果优化 void Del(Number& num) { int i,j; j=num.size; for(i=0; i<j; i++) { if(num.num[MAX-j+i]!='0') break; num.size=num.size-1; } } int main() { int i; Number number1, number2, result; while(1) { for (i=0; i<MAX; i++) { number1.num[i]='0'; number2.num[i]='0'; } Input(number1, number2); // result = ToAdd(number1,number2); result = Mult(number1,number2,number1.size); Del(result); cout<<result.sign; for(i=0; i<result.size; i++) cout<<result.num[MAX-result.size+i]; cout<<" 长度: "<<result.size<<endl; } return 0; }