从大数运算所想到的

来源:互联网 发布:纯html5游戏源码 编辑:程序博客网 时间:2024/05/01 22:04
[cpp] view plaincopy
  1. #define SHRT_MIN    (-32768)        /* minimum (signed) short value */  
  2. #define SHRT_MAX      32767         /* maximum (signed) short value */  
  3. #define USHRT_MAX     0xffff        /* maximum unsigned short value */  
  4. #define INT_MIN     (-2147483647 - 1) /* minimum (signed) int value */  
  5. #define INT_MAX       2147483647    /* maximum (signed) int value */  
  6. #define UINT_MAX      0xffffffff    /* maximum unsigned int value */  
  7. #define LONG_MIN    (-2147483647L - 1) /* minimum (signed) long value */  
  8. #define LONG_MAX      2147483647L   /* maximum (signed) long value */  
  9. #define ULONG_MAX     0xffffffffUL  /* maximum unsigned long value */  
  10. #define LLONG_MAX     9223372036854775807i64       /* maximum signed long long int value */  
  11. #define LLONG_MIN   (-9223372036854775807i64 - 1)  /* minimum signed long long int value */  
  12. #define ULLONG_MAX    0xffffffffffffffffui64       /* maximum unsigned long long int value */  

     

 

 

                  /*写这篇文章之前,先说下一下背景,最初的时候,是准备写大数运算的,当然大数运算大家都写烂了,我自然是先借鉴了网上的诸多材料,才开始准备写。然而很不幸出师未捷身先死,还没有切入算法,就在准备工作上除了诸多问题,总算花了一个下午的时间解决了所有问题,接着今天的空闲时光写出这篇日志*/

 

大数运算,顾名思义,就是很大的数值的数进行一系列的运算。在C语言中定义的数据类型都是存在最大存储值的,在limits.h中有定义

 


从上面可以看出在C语言(在我的机器上)中可以存储的最大的数是unsigned long long ,也只能达到2^17-1的最大值。那么比这更大数怎么办呢?这就牵扯到大数怎么存储的问题了。使用相应的数据结构来存储数据,使用诸如数组,链表甚至是树来储存大数。就是解决这类问题的方法。最初的选择主要是在数组与链表中考虑。

如果使用链表的话,那么未来在计算数的时候,必须不停的移动指针,来完成计算,效率未免过低/*以上存疑*/  那么使用数组呢?这时候就有许多东西需要考虑了,大尾存储,还是小尾存储。/*感兴趣的请自行google little-endianbig-endian*/还是考虑对于运算的效率。对于进位运算而言,先从低位运算开始,将进位与后边的数进行运算,这个时候如果考虑小尾的存储方式更为的合理。但是在数组之中仍是标准的存储方式。

/*后来想想如果采用vector<long long >会不会更为的合理*/

[cpp] view plaincopy
  1. #define ARRAY_SIZE 10  
  2. typedef struct LargeNumber  
  3. {  
  4.     int digit;//实际利用到的位数  
  5.     unsigned long long int array[ARRAY_SIZE];  
  6.     bool flag;//用于标记符号位  
  7. }* PLargeNumber;  
  8.    
  9.    


在这里重点要聊一聊#define ARRAY_SIZE 10

由于使用了宏定义,就会出现了一个重要的问题那就是数组的大小被固化,在这我们可以使用柔性数组

 

[cpp] view plaincopy
  1. typedef struct LargeNumber  
  2. {  
  3.     int digit;//实际利用到的位数  
  4.     unsigned long long int array[];  
  5.     bool flag;//用于标记符号位  
  6. }* PLargeNumber;  


有兴趣的朋友可以看看这个网址http://blog.csdn.net/ce123_zhouwei/article/details/8973073

 

当然在初始化的时候依旧要使用malloc realloc来扩展指针。当然嫌麻烦的可是直接使用指针unsined long long * array;/*好吧我承认,我应该在这里用vector*/

 

有了定义,就应该有初始化函数了

[cpp] view plaincopy
  1. bool LargeNumberInit(PLargeNumber* p)  
  2. {  
  3.    
  4.     *p = (PLargeNumber) malloc (sizeof(LargeNumber));  
  5.     if(NULL == (*p))  
  6.         return false;//创建失败  
  7.     else{  
  8.         for(int i=0; i!=ARRAY_SIZE; i++){  
  9.             (*p)->array[i] = 0;  
  10.         }  
  11.         (*p)->length = 0;  
  12.         return true;  
  13.     }  
  14. }  


那么接下来的问题就到了如何读取数据了。既然是大数,那么传统的cinsprinf 自然而然的被我放弃了/*其实我试过的无法单独的读取某个数*/。采用getchar()循环读取字符,在将字符转化位数字。

备选思路如下:

1.普大喜奔的位运算,16位的16进制数的unsigned long long 读取一个16进制数,采用左移的方式来读入一个数中。数组中的每一位都采用这样的方式来存储。

2.依旧是16位的做法这一次采用读取满一个16进制位,放入字符串中然后进行转化。

3.读取的是10进制数的字符,将其存入字符串中,读取满特定位数后,将其转化为数字。

 

最后本人采用了最后一种方法,原因在于在stdlib.h中有atoll
()这样的转化函数/*后来证明这个方向是错的*/

 

 

[cpp] view plaincopy
  1. PLargeNumber LargeNumberPutIn(PLargeNumber PNum)  
  2. {  
  3.     int i = 0;  
  4.     int number = 0;  
  5.     char buffer[NODESIZE+1];//定义的缓冲区存储输入的数字  
  6.     buffer[NODESIZE] = '\0';  
  7.     number = getchar();  
  8.     if(number=='-')  
  9.         PNum->flag = false;  
  10.     else{  
  11.         PNum->flag = true;  
  12.     }  
  13.     do {  
  14.           
  15.         number = getchar();  
  16.         if ((number<='9') && (number>='0')){  
  17.             buffer[i%NODESIZE] = number;/*i%NODESIZE等于每次对应的缓冲区的位数*/  
  18.             PNum->length++;  
  19.           
  20.    
  21.         if(((i%NODESIZE)==0) && i!=0){  
  22. /*每次读满NODESIZE位将数据刷入相对应的字节中*/  
  23.             int array_number = NODESIZE - i/NODESIZE - 1;  
  24.             PNum->array[array_number] = atoll(buffer);  
  25.         }  
  26.         i++;  
  27.       }    
  28.     }  
  29.     while ((number!='#') && (i!=NODESIZE*ARRAY_SIZE));  
  30.     return PNum;  
  31. }  
  32.    


首先读取第一个数,判断其是否是符号位。再循环中执行下列过程读取19个数字放置在预先准备好的字符串数组中,在使用atoll函数转化。但是在VS中不存在atoll()函数。同时使用atoll返回值是long long 

#define LLONG_MAX     9223372036854775807i64 

 

好吧我定义的的ARRAY_NODE_MAX是这个值

#define ARRAY_NODE_MAX 9999999999999999999//ULLMAX少一位

目测只能自己写这个函数了。

[cpp] view plaincopy
  1. unsigned long long atoll(char* buffer)  
  2. {  
  3.     const int longlength = 10;  
  4.     const long long tendigit = 10000000000;  
  5.     unsigned long long  number = 0;  
  6.     int buffer_length = strlen(buffer);  
  7.     const int begin_length = buffer_length - longlength;//buffer取出掉后十位后的长度  
  8.     if(buffer_length<=longlength)  
  9.         number = (unsigned long long) atol (buffer);  
  10.     else//如果大于10位的话  
  11.     {  
  12.         char dest_buffer[longlength+1];  
  13.         strcpy(dest_buffer, buffer+begin_length);//单独取出后十位  
  14.         number += atol(dest_buffer);  
  15.         for(int i=0; i!=longlength; i++)                 dest_buffer[i] = '\0';/*必须把缓冲区清空,避免上一次的数据污染*/  
  16.    
  17.        memcpy(dest_buffer,buffer,  
  18.                    sizeof(char)*begin_length);//以后十位为终点取出前边几位  
  19.         number += atol(dest_buffer)*tendigit;  
  20.     }  
  21.    
  22.     return number;  
  23. }  


 

就这样所有的准备工作都已经就绪,紧接着的就是大数运算咯。