程序员面试题精选100题<6>-大整数的乘法
题目:设X和Y都是n位的十进制整数,计算它们的乘积XY。
分析:
我们可以用小学所学的方法来设计一个计算乘积XY的算法,但是这样做计算步骤太多,显得效率较低。如果将每2个1位数的乘法或加法看作一步运算,那么这种方法要作O(n2)步运算才能求出乘积XY。下面我们用分治法来设计一个更有效的大整数乘积算法。将n位的二进制整数X和Y各分为2段,每段的长为n/2位(为简单起见,假设n是2的幂),如图所示。
图: 大整数X和Y的分段
由此,
X=A*10^(n/2)+B ,Y=C*10^(n/2)+D。
这样,X和Y的乘积为:
XY=[A*10^(n/2)+B][C*10^(n/2)+D]=AC*10^n+(AD+CB)*10^(n/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),我们有:
由此可得T(n)=O(n2)。因此,用(1)式来计算X和Y的乘积并不比小学生的方法更有效。要想改进算法的计算复杂性,必须减少乘法次数。为此我们把XY写成另一种形式:
XY=AC*10n+[(A-B)(D-C)+AC+BD]*10n/2+BD (3)
虽然,式(3)看起来比式(1)复杂些,但它仅需做3次n/2位整数的乘法(AC,BD和(A-B)(D-C)),6次加、减法和2次移位。由此可得:
用解递归方程的套用公式法马上可得其解为T(n)=O(nlog3)=O(n1.59)。利用式(3),可写出相应的代码:
由公式(3)可以看出,我们需要完成以下功能:
1> 大整数的加法
2> 大整数的减法
3> 大整数的乘法
利用分治法在计算大整数的过程,当某个中间值在int所表示的范围内时,可直接用整数的加减乘运算进行计算(同时必须保证进行这种运算的结果也在int所标示的范围内),加快计算速度。
代码:
1: #include<iostream>2: #include<vector>3: using namespace std ;4: void sub(const vector<char>& x, const vector<char>& y, vector<char>& ret) ;5: void add(const vector<char>& x, const vector<char>& y, vector<char>& ret) ;6: // int(0,1,2...9) to char
7: void itoa(const int i, char&ch)8: {9: if (i >= 0 && i <= 9){
10: ch = '0' + i ;11: }else{
12: ch=' ' ;13: }14: }15: //char to int
16: int atoi(const char ch)17: {18: if(ch>='0' && ch <='9')
19: {20: return (ch - '0') ;
21: }else{
22: return -1 ;
23: }24: }25: // string to int
26: int str2int(vector<char> str)27: {28: int ret = 0 ;
29: for(int i=0;i<str.size();++i){30: ret *= 10 ;31: ret += atoi(str[i]) ;32: }33: return ret ;
34: }35: // int to string
36: void int2str(int i,vector<char>& vCh)37: {38: int tmp = i ;
39: int j = 0 ;
40: char ch ;
41: int sign = 1 ;
42: if (tmp<0){
43: sign = -1 ;44: tmp *= -1 ;45: }46: while(tmp){
47: j = tmp % 10 ;48: tmp /= 10 ;49: itoa(j,ch) ;50: vCh.insert(vCh.begin(),ch) ;51: }52: if (sign==-1){
53: vCh.insert(vCh.begin(),'-') ;54: }55: }56: //compare the two big int num in the format of string
57: int compare(const vector<char>& x, const vector<char>& y)58: {59: char signX ,signY ;
60: signX = x.front() ;61: signY = y.front() ;62: if ('-' == signX && '-' != signY){ // x<0,y>063: return 1 ;
64: }else if ('-' != signX && '-' == signY){ // x>0 ,y<065: return -1 ;
66: }else if ('-' == signX && '-' == signY){ // x<0 ,y<067: vector<char> X(x.begin(),x.end()) ; // tmp var68: vector<char> Y(y.begin(),y.end()) ;
69: X.erase(X.begin()) ;70: Y.erase(Y.begin()) ;71: return (-1) * compare(X,Y) ;
72: }else{ // x>0 , y>073: if (x.size()>y.size()){ // x is longer74: return -1 ;
75: }else if (x.size()<y.size()) { // y is longer76: return 1 ;
77: }else { // x.szie() == y.size()78: int i=0 ;
79: for (i=0;i<x.size();++i){
80: if (x[i]<y[i]){
81: return 1 ;
82: }else if (x[i]>y[i]){83: return -1 ;
84: }else{ // x[i] == y[i]85: continue ;
86: }87: }88: if (-1==i){ // x == y89: return 0 ;
90: }91: }92: }93: }94: //if x < y return true ,else false
95: bool Less(const vector<char>& x, const vector<char>& y)96: {97: return (1==compare(x,y)) ;
98: }99: //if x > y return true, else false
100: bool Great(const vector<char>& x, const vector<char>& y)101: {102: return (-1==compare(x,y)) ;
103: }104: // if x==y return true ,else false
105: bool equal(const vector<char>& x, const vector<char>& y)106: {107: return (0==compare(x,y)) ;
108: }109: // x*10^n ==> add n '0' at the end of the string
110: void shiftLeft(vector<char>&x, const int n)111: {112: for(int i=0;i<n;++i){113: x.push_back('0') ;114: }115: }116: // del the '0' at the begining of the string
117: int delFirstZero(vector<char>&x)118: {119: vector<char>::iterator it ;
120: int i ;
121: for(i=0;i<x.size();++i){
122: if ('0'!=x[i]){
123: break ;
124: }125: }126: if (i>0){
127: it = x.begin() ;128: x.erase(it,it+i) ;129: }130: }131: // add some '0' at the begining of x or y
132: // so the value of x and y is not changed
133: // let x.size() == y.size()
134: int adjustSize(vector<char>& x,vector<char>& y)135: {136: delFirstZero(x) ;137: delFirstZero(y) ;138: //second add 0 at the front
139: int n1 = x.size() ;
140: int n2 = y.size() ;
141: int n = n1 ;
142: if (n1<n2){
143: n = n2 ;144: }145: n = (n%2==0?n:n+1) ; // n is a even num
146: n = (n>0?n:1) ; // n at least is 1
147: if (n1 != n2){
148: for (int i=0;i<n-n1;++i){149: x.insert(x.begin(),'0') ;150: }151: for (int i=0;i<n-n2;++i){152: y.insert(y.begin(),'0') ;153: }154: }155: return n ;
156: }157: // add the big int num
158: void add(const vector<char>& X, const vector<char>& Y, vector<char>& ret)159: {160: int a,b,c,r ;
161: char ch ;
162: vector<char> x(X.begin(),X.end()) ;
163: vector<char> y(Y.begin(),Y.end()) ;
164: // delete the 0 in the front of the num
165: delFirstZero(x) ;166: delFirstZero(y) ;167:168: ret.clear() ;169: if (x.empty()){ // x empty170: if(y.empty()){ // x and y is empty171: ret.push_back('0') ;172: }else{ // x is empty ,y is not empty173: ret.insert(ret.begin(),y.begin(),y.end()) ;174: }175: return ;
176: }177: if (y.empty()){ // y is empty and x is not empty178: ret.insert(ret.begin(),x.begin(),x.end()) ;179: return ;
180: }181:182: vector<char> zero(1,'0') ;
183: if(Less(x,zero) && Less(y,zero)){ // x<0,y<0184: x.erase(x.begin()) ;185: y.erase(y.begin()) ;186: add(x,y,ret) ;187: ret.insert(ret.begin(),'-') ;188: return ;
189: } else if (Less(x,zero) && Great(y,zero)){ // x<0,y>0190: x.erase(x.begin()) ;191: sub(y,x,ret) ;192: return ;
193: } else if (Great(x,zero)&& Less(y,zero)){ // x>0,y<0194: y.erase(y.begin()) ;195: sub(x,y,ret) ;196: return ;
197: }else{ // x>0,y>0198: // for optimization
199: if(x.size()<10 && y.size()<10){ //x,y < (2^31-1)/2200: int tmpX = str2int(x) ;201: int tmpY = str2int(y) ;202: int tmpR = tmpX + tmpY ;
203: int2str(tmpR,ret) ;
204: return ;
205: }206: adjustSize(x,y) ;207: c = 0 ;208: for (int i=x.size()-1;i>=0;--i){209: a = atoi(x[i]) ;210: b = atoi(y[i]) ;211: r = a + b + c ;212: c = r / 10 ; // carry, c==0 or c==1
213: itoa(r%10,ch) ;214: if (' ' != ch){
215: ret.insert(ret.begin(),ch) ;216: }217: }218: if ( 1==c ){ // the last time add ,may carry219: itoa(c,ch) ;220: if (' ' != ch){
221: ret.insert(ret.begin(),ch);222: }223: }224: }225: }226: // sub two big int num
227: void sub(const vector<char>& x,const vector<char>& y, vector<char>& ret)228: {229: int a,b,c,r ;
230: char ch ;
231: ret.clear() ;232: vector<char> X(x.begin(),x.end()) ;
233: vector<char> Y(y.begin(),y.end()) ;
234: delFirstZero(X) ;235: delFirstZero(Y) ;236: if (X.empty()){ // x empty237: if (Y.empty()){
238: ret.push_back('0') ;239: }else{
240: ret.insert(ret.begin(),Y.begin(),Y.end()) ;241: }242: return ;
243: }244: if (Y.empty()){ // Y is empty and X is not empty245: ret.insert(ret.begin(),X.begin(),X.end()) ;246: return ;
247: }248:249: vector<char> zero(1,'0') ;
250:251: if (equal(X,Y)){ // x==y252: ret.clear() ;253: ret.push_back('0') ;254: return ;
255: }256: if (Less(X,zero) && Less(Y,zero)){ // x<0,y<0257: Y.erase(Y.begin()) ;258: X.erase(X.begin()) ;259: return sub(Y,X,ret) ;
260: }else if (Less(X,zero) && Great(Y,zero)){ // x<0,y>0261: Y.insert(Y.begin(),'-') ;262: return add(X,Y,ret) ;
263: }else if (Great(X,zero) && Less(Y,zero)){ // x>0,y<0264: Y.erase(Y.begin()) ;265: return add(X,Y,ret) ;
266: }else{ // x>=0,y>=0267: if (Less(X,Y)){ // x < y268: sub(Y,X,ret) ;269: delFirstZero(ret) ;270: ret.insert(ret.begin(),'-') ;271: return ;
272: }273: // for optimization
274: if(x.size()<10 && y.size()<10){ //x,y < (2^31-1)275: int tmpX = str2int(x) ;276: int tmpY = str2int(y) ;277: int tmpR = tmpX - tmpY ;
278: int2str(tmpR,ret) ;
279: return ;
280: }281: adjustSize(X,Y) ; // x > y
282: for (int i=X.size()-1;i>=0;--i){ // from low to high283: a = atoi(X[i]) ;284: b = atoi(Y[i]) ;285: r = a - b ;286: if (r<0){ // borrow 1 from the high bit287: int j=i-1 ;
288: for (;j>=0;--j){
289: if ('0' != X[j]){
290: c = atoi(X[j]) ;291: --c ;292: itoa(c,ch) ;293: if (' ' != ch){
294: X[j] = ch ;295: }296: break ;
297: }else{// x[j] == 0 then set it to 9298: itoa(9,ch) ;299: if (' ' != ch){
300: X[j] = ch ;301: }302: }303: }304: r = r + 10 ; // 0<r<10
305: }306: itoa(r,ch) ;307: if (' ' != ch){
308: ret.insert(ret.begin(),ch) ;309: }310: }311: delFirstZero(ret) ;312: return ;
313: }314: }315: // mul two big int num
316: void mul(const vector<char>& X, const vector<char>& Y, vector<char>& result)317: {318: int sign = 1 ;
319:320: vector<char>x(X.begin(),X.end()) ;
321: vector<char>y(Y.begin(),Y.end()) ;
322:323: delFirstZero(x) ;324: delFirstZero(y) ;325: // any of the x,y is empty ,set ret to '0'
326: if (x.empty() || y.empty()){
327: result.clear() ;328: result.push_back('0') ;329: return ;
330: }331: // get the sign of the result
332: if ('-' == x.front()){ // x is negative333: sign *= -1 ;334: x.erase(x.begin()) ;335: }336: if ('-' == y.front()){ //both x and y is negative337: sign *= -1 ;338: y.erase(y.begin()) ;339: }340:341: // the exit condition
342:343: if (x.size()<=5 && y.size()<=5){
344: int a = str2int(x) ;345: int b = str2int(y) ;346: if (a<46340 && b< 46340){
347: result.clear() ;348: int2str(a*b*sign,result) ;
349: return ;
350: }351: }352:353: // adjust x,y , let the size of x and y is even
354: adjustSize(x,y) ;355:356: vector<char> A(x.begin(),x.begin()+x.size()/2) ;
357: vector<char> B(x.begin()+x.size()/2,x.end()) ;
358: vector<char> C(y.begin(),y.begin()+y.size()/2) ;
359: vector<char> D(y.begin()+y.size()/2,y.end()) ;
360: // result = A*C*10^n +[(A-B)*(D-C)+AC+BD]*10^(n/2)+BD
361: // declaration some tmp var for compute
362: vector<char> AC ; // A * C363: vector<char> BD ; // B * D364: vector<char> ABDC ; // (A-B) * (D-C)365: vector<char> v1; // AC*10^n366: vector<char> v2 ; // [(A-B)*(D-C)+AC+BD]*10^(n/2)367: vector<char> v3 ; // v3 = v1 + v2368: vector<char> subAB ; // A - B369: vector<char> subDC ; // D - C370: vector<char> add2 ; // (A-B)*(D-C)+AC371: vector<char> add3 ; // (A-B)*(D-C)+AC+BD372:373: mul(A,C,AC) ;374: mul(B,D,BD) ;375: sub(A,B,subAB) ;376: sub(D,C,subDC) ;377: mul(subAB,subDC,ABDC) ;378: add(ABDC,AC,add2) ;379: add(add2,BD,add3) ;380:381: int size = x.size() ;
382: if (1==size%2){
383: ++size ;384: }385: shiftLeft(AC,size) ;386: shiftLeft(add3,size/2) ;387:388: add(AC,add3,v3) ;389: add(v3,BD,result) ;390:391: delFirstZero(result) ;392: if (-1==sign){
393: result.insert(result.begin(),'-') ;394: }395: }396: