转载:大数运算

这里的大数,指的是超过计算机CPU寄存器表达的数,即超过计算机字长的数。大数运算主要指的是对大数进行数论运算,如加、减、乘、除。出于效率原因,一般的大数运算主要指对无符号类型的数进行数论计算。

通过我收集的一些资料和ACM的一些知识。总结:

(1)、加法用数组模拟,低位加起,带一个进位;

(2)、减法用数组模拟,低位减起,带一个借位;

(3)、乘法用数组模拟,乘数每一位乘以被乘数结果保留;(i+j就是结果的位);

(4)、除法用数组模拟,累减,不过是通过笔算的除法,从高位减起,不足就从后加数;

有整理的代码,是转载的,原来的网址不记得了,本来没有缩进的我修改了一下,好看些!

 

  1 /*------------------------------------------------------------------------------
  2 *函数名称: 大数加法
  3 *函数过程:
  4 * 1 比较两个数那一个长
  5 * 2 以长的作为循环次数
  6 * 3 对应项相加 进位存贮直到下高位相加用
  7 * 4 直到循环结束
  8 * 5 !!!!!!没设计负数相加
  9 * 6 加数,被加数是结果都是顺序的,高位到低位
 10 *入口参数:numa,numb,result字符串
 11 *出口参数:无
 12 *编辑环境:winSP2 + VC2003 + C++
 13 *-------------------------------------------------------------------------------
 14 */
 15 void addition(char *numa, char *numb,char *result) // 计算两大数之和
 16 {
 17   char *pna = findend(numa); // 指向numa的一个指针。pna 指向加数的最低位,
 18   char *pnb = findend(numb); //指向numb的一个指针。 pnb 指向被加数的最低位,
 19   int along=(int)strlen(numa); //标记数字a的长度;
 20   int blong=(int)strlen(numb); //标记数字b的长度;
 21   int times = 0; // 标致要计算多少次。
 22   int carry=0,temp_result; //存贮进位 和临时结果的
 23   Node *head,   // 用于存贮头指针
 24        *pstart, // 用于存贮计算时的首指针
 25        *pnew;   //作于申请新结点 
 26   head = pstart =new Node; //初始化首结点和头结点。
 27   pstart -> data = 0;
 28   pstart -> next = NULL;
 29   pstart -> ahead = NULL; 
 30 
 31   if (abigerb(numa ,numb)>=1)
 32     times = (int)strlen(numa); //比较两个字符串长度,以大的作为循环次数
 33   else {
 34     times = (int)strlen(numb);
 35     pna = findend(numb); //交换指针
 36     pnb = findend(numa);
 37     along=(int)strlen(numb); //标记数字a的长度;
 38     blong=(int)strlen(numa); //标记数字b的长度;
 39   }
 40   while ((times-- && (times>=0))|| carry != 0) { 
 41     if(!pstart->next) //如果当前为空结点,则申请新结点
 42     {
 43       pnew = new Node;
 44       pnew -> data = 0;
 45       pnew -> next = NULL;
 46       pnew -> ahead = pstart;
 47       pstart -> next = pnew;
 48     }
 49     else //自身的值+新值+进位 作为当前的新值
 50       temp_result =(pstart->data +(*pna-48)+(*pnb-48)+carry) ; 
 51 
 52     pstart -> data = temp_result%10; //存贮个位
 53     carry = temp_result/10; //存贮进位
 54     pstart = pstart -> next; //结点移动
 55     blong--;
 56     if(blong>0)pnb--; //指针移向被加数高位
 57     else *pnb=48; //之后相减就变为了0不作任何运算;
 58     pna--; //加数指针移动,
 59   }
 60   pstart =head; //寻找链表的结尾点
 61   while(pstart->next != 0) {
 62     pstart->data += 48; //!!<<<因为我们的输出是字符。所以再此加上48>>>> 逆顺输出
 63     pstart = pstart->next ;
 64   }
 65 
 66   int tip = 0; //转为字符串用
 67   pstart = pstart->ahead ; //找有效字
 68    //cout<<"/n结果是 : ";
 69   while(pstart != 0) //输出正序的结果;
 70   {
 71     result[tip++] = pstart->data; 
 72      //cout< data;
 73     pstart = pstart->ahead ;
 74   }
 75   result[tip] = '/0';
 76 
 77   pstart =head; //释放空间
 78   while(pstart->next != 0) {
 79     pnew = pstart->next ;delete pstart;
 80     pstart =pnew; 
 81   }
 82   return ;
 83 }
 84 //返回值说明:0是alongblong ; 2是along=blong
 85 int abigerb(char *numa, char *numb) //比较两个数最高位那一个大
 86 {
 87   int along=(int)strlen(numa); //标记数字a的长度;
 88   int blong=(int)strlen(numb); //标记数字b的长度;
 89   char *pna = numa; //指向数A的最高位,
 90   char *pnb = numb; //指向数B的最高位,
 91   if (along>blong) return 1;
 92   if (along==blong)
 93   {
 94     while(*pna) //比较两个数那一个大
 95     {
 96       if(*pna>*pnb)return 1;
 97       else if(*pna<*pnb)return 0 ;
 98       else if(*pna==*pnb){pna++;pnb++;} //1111与1112 
 99     }
100     return 2; //这表明找到最后了还是全相等;
101   }
102   return 0 ;
103 }
104 /*------------------------------------------------------------------------------
105 *函数名称: 大数减法
106 *函数过程:
107 * 1 比较两个数哪一个长
108 * 2 以长的作为循环次数
109 * 3 如果两个数长度相等,从最高位开始比直到发现那一个数更大,使大项减去小项
110 * 4 对应项相减 进位存贮直到下高位相加用
111 * 5 每一位对应项相减时,处理三种可能的情况,a=b,a>b;
112 * 6 a<b时,则计算,11-12,111-112,要考虑借位
113 * 7 直到循环结束
114 *入口参数:numa,numb,result字符串
115 *出口参数:无
116 *-------------------------------------------------------------------------------
117 */
118 void subtract(char *numa, char *numb,char *result)//计算减 
119 { 
120   char *pna = findend(numa);//指向numa的一个指针,pna 指向减数的最低位, 
121   char *pnb = findend(numb);//指向numb的一个指针,pnb 指向被减数的最低位, 
122   int along=(int)strlen(numa);//标记数字a的长度; 
123   int blong=(int)strlen(numb);//标记数字b的长度; 
124   int times = 0; // 标记要计算多少次。 
125   int carry=0; //存贮借位 
126   int clear0=0; //消除结果最前面无用的'0' 13-5 = 08 的效果!! 
127   int isnegative=0; //用来加上被减数大于减数时补上最后一个负号
128   Node *head, // 用于存贮头指针 
129        *pstart, // 用于存贮计算时的首指针 
130        *pnew; //作于申请新结点 
131   head = pstart =new Node;//初始化首结点和头结点。 
132   pstart -> data = 0;
133   pstart -> next = NULL;
134   pstart -> ahead = NULL; 
135   if (abigerb(numa ,numb))
136     times = strlen(numa);//比较两个字符串长度,以大的作为循环次数
137   else //交换位置以降低难度
138   { 
139     times = strlen(numb);//让数(字符串)长的减去数(字符串)短的
140     pna = findend(numb);//交换指针 
141     pnb = findend(numa); along=(int)strlen(numb);//标记数字a的长度;
142     blong=(int)strlen(numa);//标记数字b的长度; 
143     isnegative=1;//标记最后要加上负号 
144   } 
145   while ((times-- && (times>=0))|| carry != 0)//carry != 0 说没有借位时
146   { 
147     if(!pstart->next)//如果当前为空结点,则申请新结点 
148     { 
149       pnew = new Node; 
150       pnew -> data = 0;
151       pnew -> next = NULL; 
152       pnew -> ahead = pstart; 
153       pstart -> next = pnew; 
154     } 
155     if(times<0)//如果计算完之后,借位等于1,,说明必然为负值;
156     { 
157       pstart -> data = -3 ;//让它等于负号 '-'//-3来源于负号与0相差3。。 
158       break; 
159     } 
160     else { 
161       if ( *pna == *pnb )//减数等于被减数时。结果等于直截相减的结果;并置借位为0
162       { 
163         if(carry==0)
164           pstart -> data = (*pna-48)-(*pnb-48); //111-11的情况 
165         else { 
166           pstart->data = (*pna-48)-(*pnb-48)+10 -carry;//1121-1112 
167           carry=1; 
168         } 
169       } 
170       if( *pna > *pnb )//减数大于被减数时。结果等于直截相减的结果;并置借位为0 
171       { 
172         pstart -> data = (*pna-48)-(*pnb-48)-carry; //存贮个位
173         carry=0; 
174       } 
175       else if( *pna < *pnb )//说明被减数大于减数让结果加10相当于借位 (carry)为1 
176       { 
177         if(times>0) 
178           pstart->data = (*pna-48)-(*pnb-48)+10 -carry;//13-5的情况作为新值
179         else 
180           pstart->data = (*pnb-48)-(*pna-48)-carry; //3-5作为当前的新值carry=1; 
181       } 
182     } 
183     pstart = pstart -> next; //结点移动 blong--; 
184     if(blong>0)
185       pnb--;//指针移向被减数高位 
186     else 
187       *pnb=48; //之后相减就变为了0不作任何运算,其实可以优化的。
188               //但代码会长!而且还需要重新开结点。所以放弃; 
189     pna--;//被数指针移动, 
190   } 
191   if(isnegative==1)////加上负号处理。增加一长度并置为负号
192   { 
193     pnew = new Node; 
194     pnew -> data = 0;
195     pnew -> next = NULL; 
196     pnew -> ahead = pstart; 
197     pstart -> next = pnew; 
198     pstart->data=-3;//因为寻找链表的结尾点要统一加48。又因为‘-’是45。所以等于‘-3’
199   } 
200   pstart =head;//寻找链表的结尾点 
201   while(pstart->next != 0) { 
202     pstart->data += 48;//!!<<<因为我们的输出是字符。所以再此加上48>>>> 逆顺输出 
203     pstart = pstart->next ; 
204   } 
205   int tip = 0;//转为字符串用 
206   clear0=0;// 消除结果最前面无用的'0' 13-5 = 08 的效果 ..>>修改字符串的首指针
207   pstart = pstart->ahead ;//找有效字 
208   while(pstart != 0)//输出正序的结果;
209   { 
210     if (clear0==0 && ((int)pstart->data)==48&&pstart->ahead!=0)
211     // 消除结果最前面无用的'0' ;//不输出任何东西 
212     else 
213       result[tip++] = pstart->data; 
214     if(((int)pstart->data)!=48&&((int)pstart->data)!=45)
215       clear0=1;//'-'号 
216     pstart = pstart->ahead ; 
217   } 
218   result[tip] = '\0'; 
219   pstart =head; //释放空间 
220   while(pstart->next != 0) {
221     pnew = pstart->next ;
222     delete pstart;
223     pstart =pnew; 
224   }
225   return ; 
226 }
227  /*-----------------------------------------------------------------------------
228 *函数名称: 大数乘法
229 *函数过程:
230 * 1 输入两个大数作为字符串
231 * 2 作一个双向链表
232 * 3 两个指针分别指向数字字符串的最低位
233 * 4 以第一个数的最低的一个位乘以第二个数的所有项存于链表中
234 * 5 链表首指针移
235 * 6 重复4,5依次从最低位乘到最高位
236 * 7 乘完后因为最低位是链表首,最后一位是链表尾。所以在逆顺输出链表。
237 * 4 直到循环结束
238 *入口参数:numa,numb,result字符串
239 *出口参数:无
240 *-------------------------------------------------------------------------------
241 */
242 void multiply(char *numa, char *numb ,char *result)//计算乘积
243 { 
244   char *pna = findend(numa);//指向numa的一个指针, pna 指向乘数的最低位, 
245   char *pnb = findend(numb);//指向numb的一个指针, pnb 指向被乘数的最低位, 
246   int along=(int)strlen(numa);//标记数字a的长度; 
247   int blong=(int)strlen(numb);//标记数字b的长度; 
248   int carry=0,temp_result;//存贮进位 和临时结果的 
249   Node *head, // 用于存贮头指针 
250        *pstart, // 用于存贮计算时的首指针 
251        *pnew, //作于申请新结点 
252        *pgo; //作为每计算完一行时,回到下一行起始节点用,移位标致来用 
253   head = pstart =new Node;//初始化首结点和头结点。
254   pstart -> data = 0; 
255   pstart -> next = NULL; 
256   pstart -> ahead = NULL; 
257   while (along--) { 
258     pgo = pstart;//保存进位点 
259     blong = (int)strlen(numb);//初始化长度
260     pnb = findend(numb); //初始化指针 
261     while ((blong-- && (blong>=0))|| carry != 0) {
262       if(!pstart->next)//如果当前为空结点,则申请新结点 
263       { 
264         pnew = new Node; 
265         pnew -> data = 0; 
266         pnew -> next = NULL;
267         pnew -> ahead = pstart; 
268         pstart -> next = pnew; 
269       } 
270       if(blong<0)
271         temp_result = carry ;//处理只有进位的情况 
272       else //自身值+新值+进位作为新值 
273         temp_result =(pstart->data+(*pna-48)*(*pnb-48)+carry);
274       pstart -> data = temp_result%10; //存贮个位 
275       carry = temp_result/10; //存贮进位 
276       pstart = pstart -> next; //结点移动 
277       pnb--; //指针移向被乘数高位
278     } 
279     pstart = pgo->next; //前进一个位置;
280     pna--; //指针移向乘数高位 
281   } 
282   pstart =head;//寻找链表的结尾点 
283   while(pstart->next != 0) { 
284     pstart->data += 48;//!!<<<因为我们的输出是字符。所以再此加上48>>>> 逆顺输出
285     pstart = pstart->next ; 
286   } 
287   int tip = 0;//转为字符串用 
288   pstart = pstart->ahead ;//找有效字 
289   while(pstart != 0)//输出正序的结果; 
290   { 
291     result[tip++] = pstart->data; 
292     pstart = pstart->ahead ; 
293   } 
294   result[tip] = '\0'; 
295   pstart =head; //释放空间 
296   while(pstart->next != 0) { 
297     pnew = pstart->next ;
298     delete pstart; pstart =pnew; 
299   } 
300   return ; 
301 }
302 
303 /*------------------------------------------------------------------------------
304 *函数名称: 大数除法2-- 
305 *函数想法:
306 * 1 用指针指向除数的最高位,保存临时字符串; tempstr[a++] = pna
307 * 2 如果临时字符串大于被除数,则相减。结果等于余数
308 * if(tempstr>numb)tempstr = tempstr - numb
309 * 3 如果小于被除数且指针到头,余数 等于 临时字符串
310 * if(tempstr * 
311 *入口参数:numa,numb,result,remainder字符串
312 *出口参数:无
313 *-------------------------------------------------------------------------------
314 */
315 void divide2( char *numa, char *numb,char *result,char *remainder)//计算除法2 
316 { 
317   char one[]="1";//临时字符串.... 
318   char one2[]="1";// 
319   char zero[]="0";// 
320   char numb2[6048];// 
321   char tempstr[6018]="";//临时字符串 
322   int ia=0,ia2=0;//tempstr的指示器 
323   bool moveon=false;//翻转牌 
324   char *pna = numa;//指向numa的一个指针。point numa pna 指向减数的最高位, 
325   char *pnb = findend(numb);//指向numb的一个指针 //pnb 指向被减数的最低位, 
326   Node *head, // 用于存贮头指针 
327        *pstart, // 用于存贮计算时的首指针 
328        *pnew; //作于申请新结点 
329   head = pstart =new Node;//初始化首结点和头结点。
330   pstart -> data = 0; 
331   pstart -> next = NULL;
332   pstart -> ahead = NULL;
333   moveon = false; 
334   while(*pna) {
335     if(!pstart->next)//如果当前为空结点,则申请新结点 
336     { 
337       pnew = new Node; 
338       pnew -> data = 0;
339       pnew -> next = NULL;
340       pnew -> ahead = pstart;
341       pstart -> next = pnew; 
342     } 
343     ia=(int)strlen(tempstr);//取的长度
344     tempstr[ia++] = *(pna++);
345     tempstr[ia] ='/0'; //转换为字符串 
346     if(tempstr[0]=='0')//处理高位也是0的那种 如00 
347     {
348       ia2=0;
349       while(tempstr[ia2]=='0')
350         ++ia2; 
351       while(ia2>=1)//清除无用的0 
352       { 
353         ia=ia2-1; 
354         tempstr[ia]=tempstr[ia2]; 
355         --ia2; 
356       } 
357       tempstr[++ia2]='/0'; 
358     } 
359     while(abigerb(tempstr,numb)>0)//如果tempstr大于等于numb 
360     { 
361       if(tempstr[0]=='0')//处理高位也是0的那种 如00----此乃冗余代码,留做记念用 
362       {
363         ia2=0; 
364         while(tempstr[ia2]=='0')
365           ++ia2;
366         if(ia==ia2 ) { 
367           moveon = true; 
368           break;
369         } 
370       } 
371       strcpy(numb2,numb);
372       subtract(tempstr, numb,tempstr);//A-B 
373       strcpy(numb,numb2); 
374       if(tempstr[0]=='-')//若判断的不准,变为了负数就再加上B。。 
375       { 
376         strcpy(numb2,numb); 
377         addition(tempstr, numb,tempstr);//A-B 
378         strcpy(numb,numb2); 
379         ia2=0; //修正之后的长度。因为加法中未做负数运算 
380         ia=0; //为了消除最后的那一个负号,整体向前移动。
381         while(tempstr[ia2]!='/0')
382           ++ia2; 
383         while(ia2>=1)//清除无用的0
384         { 
385           tempstr[ia]=tempstr[ia+1]; 
386           ++ia; 
387           --ia2; 
388         } 
389         tempstr[ia]='\0'; 
390         moveon = true; 
391         break; 
392       }
393       pstart->data++ ; //结果自加
394       oveon = true; 
395     } 
396     if(moveon) 
397       pstart = pstart -> next; //结点移动 
398   } 
399   strcpy(remainder,tempstr);//存贮余数 
400   int tip = 0;//转为字符串用 
401   pstart =head;//寻找链表的结尾点 
402   while(pstart->next != 0) { 
403     pstart->data += 48;//!!<<<因为我们的输出是字符。所以再此加上48>>>> 逆顺输出 
404     result[tip++] = pstart->data; 
405     pstart = pstart->next ; 
406   } 
407   result[tip] = '/0';//存贮结果 
408   pstart =head; //释放空间 
409   while(pstart->next != 0) { 
410     pnew = pstart->next ;
411     delete pstart; 
412     pstart =pnew;
413   } 
414   return ; 
415 }
416 
417 /*--------------------------------------------------------------------------
418 *函数名称: 大数幂
419 *函数想法:
420 * 1 B自减1直到,,作为循环控制变量.保存结果; 
421 * 2 结果初值1 每次乘以A
422 * 3 重复1、2步骤到结束. 
423 *入口参数:numa,numb,result字符串
424 *出口参数:无
425 *--------------------------------------------------------------------------*/
426 void power (char *numa, char *numb,char *result) //计算幂
427 { 
428   char one[]="1"; //临时字符串....
429   char one2[]="1"; 
430   char zero[]="0"; 
431   char numb2[6048]; 
432   char remainder[6048]; 
433   strcpy(result,one); //给结果初值为1
434   strcpy(numb2,numb);
435   
436   subtract(numb,one ,remainder); //B自减1
437   strcpy(numb,numb2);
438   
439   strcpy(numb2,numb);
440   multiply(result,numa,result ); //A自己乘自己
441   strcpy(numb,numb2); 
442   
443   while(strcmp(remainder,zero)>0) //A大于相等B时 就是numb不等于0时
444   {
445     strcpy(numb2,numb);
446     multiply(result,numa,result ); //A自己乘自己
447     strcpy(numb,numb2); 
448     strcpy(one,one2);
449     subtract(remainder,one ,remainder); //B减一
450   }
451   
452   return ;
453 }
posted @ 2012-11-15 23:08  爪哇公民  阅读(223)  评论(0)    收藏  举报