转载:大数运算
这里的大数,指的是超过计算机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 }

浙公网安备 33010602011771号