北大OJ 1001题

题目:输入一序列的正实数和幂次(正整数)对,然后打印结果(具体的比这个精细)

这道题是关于大数计算的(大数求幂),从开始建立思路,到写代码、调式到最后被AC以及最终的优化,总共用了差不多一天的时间。开始AC时使用空间500K,时间37MS,最后一次AC空间400K,时间0MS,有很大提高。这主要归功于加大了每次的数据处理量,减少了重计算次数以及降低循环代码量。还有就是在使用了二分递归,避免了重复计算。不好的一点是代码量太大,并且使用了太多的变量。

不管怎么样,为这道题付出了很多想法,后来的一些大的优化主要来自对底层的深入理解,代码的整体实现粒度是很细的,阅读起来可能会有些困难,但很是值得推敲的,具体实现代码如下:

 

[cpp] view plain copy
 
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4.   
  5. void add(char *s1, char *s2);  
  6. char *multi(char *s1, char *s2);  
  7. char *result(char *s, int n);  
  8.   
  9. int main(void) {  
  10.     char ch[100];   
  11.     char *res;  
  12.     int  num, np;  
  13.     char tem, *temp1, *temp2;  
  14.     char *chp;  
  15.   
  16.     while ( scanf("%s%d", ch, &num) != EOF ) {  
  17.         chp = ch + strspn(ch, "0");           // 去掉前导0  
  18.         temp1 = &ch[strlen(ch)-1];  
  19.         if ( (temp2 = strchr(ch, '.')) != NULL ) {  // 如果有小数点  
  20.             while ( *temp1 == '0' )  // 去掉小数末尾的0  
  21.                 *temp1-- = 0;  
  22.             np  = strlen(temp2) - 1;  
  23.             np *= num;                  // 小数位的num倍是最终结果的小数位数  
  24.             memmove(temp2, temp2+1, strlen(temp2));  // 去掉小数点  
  25.         }  
  26.         else  
  27.             np = 0;             // 整数  
  28.   
  29.         res   = result(chp, num);  
  30. //        printf("res: %s\n", res);  
  31.         temp1 = res + strspn(res, "0");  
  32.         temp2 = &res[strlen(res) - np];     // 定位小数点  
  33.         if ( temp1 >= temp2 && np != 0 )   // 如果结果小于1  
  34.             printf("%c%s\n"'.', temp2);  
  35.         else if ( np == 0 )              // 如果是整数  
  36.             printf("%s\n", temp1 == temp2 ? 0 : temp1);  
  37.         else {  
  38.             tem   = *temp2;         // 将该放小数点位置的源字符保存  
  39.             *temp2++ = 0;           // 这里将源结果字符串断开,块式输出效率高  
  40.             printf("%s%c%c%s\n", temp1,   
  41.                     '.', tem,  
  42.                     *temp2 == 0 ? "" : temp2);  
  43.         }  
  44.   
  45.         free(res);  
  46.     }  
  47.   
  48.     return 0;  
  49. }  
  50.   
  51. char *result(char *s, int n) {  
  52.     char *res, *ch1, *ch2;  
  53.   
  54.     if ( n == 1 )   
  55.         return multi(s, "1");  // 返回统一类型的可被free掉的数据空间  
  56.     else if ( n == 2 )   
  57.         return multi(s, s);  
  58.     else if ( n > 2 ) {  
  59.         ch1 = result(s, n >> 1);  // 二分递归计算  
  60.         if ( n % 2 != 0 ) {  
  61.             ch2 = result(s, n - (n >> 1));  
  62.             res = multi(ch1, ch2);  
  63.             free(ch2);   // result函数返回值得释放掉  
  64.         }  
  65.         else   // 如果n是偶数,可避免重复计算  
  66.             res = multi(ch1, ch1);  
  67.         free(ch1);       
  68.         return res;  
  69.     }  
  70. }  
  71.   
  72. char *multi(char *s1, char *s2) {  
  73.     int  i1, i2;   
  74.     char *ch1, *ch2, *cp1, *cp2, *cp3;  
  75.     char chp[18];  
  76.     int  i, j, num, dis;  
  77.     long long j1, j2, j3; // 加大每次计算量  
  78.   
  79.     i1 = strlen(s1);  
  80.     i2 = strlen(s2);  
  81.   
  82.     ch1 = (char *)malloc(i1 + i2 + 2);  // 1 bit '\0', 1 carry bit(reserved for)  
  83.     if ( strncmp(s2, "1", 1) == 0 && i2 == 1 ) {  
  84.         memcpy(ch1, s1, i1+1);  
  85.         return ch1;  
  86.     }  
  87.     ch2 = (char *)malloc(i1 + i2 + 1);  // 1 bit '\0'  
  88.     memset(ch1, '0', i1 + i2 + 2);  
  89.     ch1[i1+i2+1] = 0;  
  90.   
  91.     i = i2;  
  92.     while ( i > 0 ) {  
  93.         if ( i >= 8 )           // 和j,每次各可处理8位  
  94.             dis = 8;  
  95.         else  
  96.             dis = i;  
  97.         i -= dis;  
  98.         memset(ch2, '0', i1 + i2 + 1);  // ch2每次循环都可能被修改  
  99.         ch2[i1+i2] = 0;  
  100.         cp1 = &s1[i1];  
  101.         cp2 = &s2[i2];  
  102.         cp3 = &ch2[i1 + i];  //  i1+i2-(i2-i)=i1+i, 每次循环往左移动dis位,表示和记录进位  
  103.           
  104.   
  105.         memcpy(chp, cp2 - i2 + i, dis);  
  106.         chp[dis] = 0;  
  107.         j2     = atoi(chp);  
  108.   
  109.         j = i1;  
  110.         while ( j > 0 ) {  
  111.             if ( j >= 8 )     // 最多8位迭代处理与j2相乘  
  112.                 num = 8;  
  113.             else   
  114.                 num = j;  
  115.             cp1 -= num;  
  116.             memcpy(chp, cp1, num);  
  117.             chp[num] = 0;  
  118.             j1       = atoi(chp);  
  119.   
  120.             memcpy(chp, cp3, dis);  // cp3记录进位,最多有dis位  
  121.             chp[dis] = 0;  
  122.             j3     = atoi(chp);  
  123.   
  124.             snprintf(chp, 18, "%lld", j1 * j2 + j3);  
  125.             j1 = strlen(chp);    
  126.             memcpy(cp3 -j1 + dis, chp, j1);     // 数据向右对齐  
  127.             cp3 -= num;  // 定位到下次计算进位可能占据空间的开头地址  
  128.             j -= num;  
  129.         }  
  130.   
  131.         add(ch1, ch2);   // 将新的计算结果与前面的相加,最后可获得最后结果  
  132.     }  
  133.   
  134.     free(ch2);  
  135.     return ch1;  
  136. }  
  137.   
  138. void add(char *s1, char *s2) {  
  139.     char *cp1, *cp2, *cp3, *ch;  
  140.     char chp[18];  
  141.     int  num, n1, n2;  
  142.     long long i, j, k;   
  143.   
  144.     s2 += strspn(s2, "0");  
  145.     n1  = strlen(s1);       // make sure n1 > n2  
  146.     if ( (n2  = strlen(s2)) == 0 )   
  147.         return;  
  148.   
  149.     ch  = (char *)malloc(n1+1);  
  150.     memset(ch, '0', n1);  
  151.     ch[n1] = 0;  
  152.   
  153.     cp1 = &s1[n1];  
  154.     cp2 = &s2[n2];  
  155.     cp3 = &ch[n1 - 1];  
  156.     while ( n2 > 0 ) {    // must validate enough memory  
  157.         if ( n2 >= 16 )   
  158.             num = 16;  
  159.         else   
  160.             num = n2;  
  161.         cp1 -= num;  
  162.         cp2 -= num;  
  163.         memcpy(chp, cp1, num);  
  164.         chp[num] = 0;  
  165.         i        = atoll(chp);  
  166.   
  167.         memcpy(chp, cp2, num);  
  168.         chp[num] = 0;  
  169.         j        = atoll(chp);  
  170.       
  171.         memcpy(chp, cp3, 1);  
  172.         chp[1] = 0;  
  173.         k      = atoll(chp);  
  174.   
  175.         snprintf(chp, 18, "%lld", i + j + k);  
  176.   
  177.         i = strlen(chp);  
  178.         cp3 -= i - 1;  
  179.         memcpy(cp3, chp, i);  
  180.         cp3 += i - 1 - num;  
  181.   
  182.         n2 -= num;  
  183.     }  
  184.   
  185.     memcpy(s1, ch, n1);  
  186.     free(ch);  
  187. }  

 

 

 

 

原文转自 http://blog.csdn.net/chiichen/article/details/6685858

原作者为 chiichen. 请尊重原作者版权

 

posted @ 2016-10-11 13:23  LonelyEnvoy  阅读(867)  评论(0编辑  收藏  举报