题目:设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),可写出相应的代码:
66算法分析
由公式(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>0
63: return 1 ;
64: }else if ('-' != signX && '-' == signY){ // x>0 ,y<0
65: return -1 ;
66: }else if ('-' == signX && '-' == signY){ // x<0 ,y<0
67: vector<char> X(x.begin(),x.end()) ; // tmp var
68: 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>0
73: if (x.size()>y.size()){ // x is longer
74: return -1 ;
75: }else if (x.size()<y.size()) { // y is longer
76: 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 == y
89: 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 empty
170: if(y.empty()){ // x and y is empty
171: ret.push_back('0') ;
172: }else{ // x is empty ,y is not empty
173: ret.insert(ret.begin(),y.begin(),y.end()) ;
174: }
175: return ;
176: }
177: if (y.empty()){ // y is empty and x is not empty
178: 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<0
184: 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>0
190: x.erase(x.begin()) ;
191: sub(y,x,ret) ;
192: return ;
193: } else if (Great(x,zero)&& Less(y,zero)){ // x>0,y<0
194: y.erase(y.begin()) ;
195: sub(x,y,ret) ;
196: return ;
197: }else{ // x>0,y>0
198: // for optimization
199: if(x.size()<10 && y.size()<10){ //x,y < (2^31-1)/2
200: 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 carry
219: 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 empty
237: 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 empty
245: 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==y
252: ret.clear() ;
253: ret.push_back('0') ;
254: return ;
255: }
256: if (Less(X,zero) && Less(Y,zero)){ // x<0,y<0
257: 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>0
261: Y.insert(Y.begin(),'-') ;
262: return add(X,Y,ret) ;
263: }else if (Great(X,zero) && Less(Y,zero)){ // x>0,y<0
264: Y.erase(Y.begin()) ;
265: return add(X,Y,ret) ;
266: }else{ // x>=0,y>=0
267: if (Less(X,Y)){ // x < y
268: 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 high
283: a = atoi(X[i]) ;
284: b = atoi(Y[i]) ;
285: r = a - b ;
286: if (r<0){ // borrow 1 from the high bit
287: 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 9
298: 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 negative
333: sign *= -1 ;
334: x.erase(x.begin()) ;
335: }
336: if ('-' == y.front()){ //both x and y is negative
337: 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 * C
363: vector<char> BD ; // B * D
364: vector<char> ABDC ; // (A-B) * (D-C)
365: vector<char> v1; // AC*10^n
366: vector<char> v2 ; // [(A-B)*(D-C)+AC+BD]*10^(n/2)
367: vector<char> v3 ; // v3 = v1 + v2
368: vector<char> subAB ; // A - B
369: vector<char> subDC ; // D - C
370: vector<char> add2 ; // (A-B)*(D-C)+AC
371: vector<char> add3 ; // (A-B)*(D-C)+AC+BD
372:
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:
本文介绍了一种使用分治法优化大整数乘法的高效算法,并提供了详细的代码实现。该算法通过减少乘法次数,将计算复杂度从O(n^2)降低到O(n^1.59)。



1658

被折叠的 条评论
为什么被折叠?



