大整数运算(算法笔记)
对于一道A+B的题目,如果A和B的范围在int范围内,那么是很简单的。但是如果A和B都是超过100个数位的整数,那么问题就变得复杂了。
今天,我借鉴《算法笔记》中的算法,总结下大整数运算的技巧。
一、大整数的存储
很简单,使用数组即可。如将整数23581存储到数组中,则有如下数组图:
为什么要反过来存储呢?是因为我们在进行整数运算的时候都是从低位到高位进行枚举,这叫做顺位存储。但是顺位存储在读入的时候并不是顺位读入的。
当我们按照字符串%s读入的时候,实际上是逆位存储的,因此在读入之后应当进行逆置处理。
为了方便随时获取大整数的长度,一般都会定义一个int型变量len来记录其长度,并组成结构体
1 struct bign{ 2 int d[10000]; 3 int len; 4 };
而输入大整数时,一般都是先用字符串读入,然后再把字符串另存至bign结构体。根据以上特点,读入字符串之后,我们应当逆序存储数组中。
1 bign change(char str[]){
memset(a.d,0,1000*sizeof(int)); 2 bign a; 3 a.len = strlen(str); 4 5 int i; 6 for(i=0;i<a.len;i++){ 7 a.d[i] = str[a.len-1-i]; 8 } 9 return a; 10 }
如果要比较两个bign变量的大小,规则也很简单:先判断两者的len大小,如果不相等,则以长的为大;如果相等,则从高位到低位进行判断。
1 int compare(bign a,bign b){ 2 if(a.len>b.len){ 3 return 1; 4 }else if(a.len<b.len){ 5 return -1; 6 }else{ 7 int i; 8 for(i=a.len-1;i>=0;i--){ 9 if(a.d[i]>b.d[i]){ 10 return 1; 11 }else if(a.d[i]<b.d[i]){ 12 return -1; 13 } 14 } 15 } 16 return 0; 17 }
大整数的四则运算(+、-、*、/)
1、高精度加法
模拟小学生手法(记着对结构体中的成员变量初始化0)
1 bign add(bign a, bign b){ 2 bign c;
memset(c.d,0,1000*sizeof(int));
c.len = 0;
3 int carry = 0; //carry进位 4 int i; 5 for(i=0;i<a.len||i<b.len;i++){//以较长的为界限 6 int temp = a.d[i]+b.d[i]+carry; 7 c.d[c.len++] = temp%10; 8 carry = temp/10; 9 } 10 if(carry!=0){ 11 c.d[c.len++] = carry; 12 } 13 return c; 14 }
2、高精度减法
值得注意的是,在高精度加法中一定存在这样的运算
a=1234,b=-3456;
这样我们其实可以将b转换为正数,然后进行高精度减法,下面介绍高精度减法
1 bign sub(bign a,bign b){ 2 bign c; 3 memset(c.d,0,1000*sizeof(int)); 4 c.len = 0; 5 6 int i; 7 for(i=0;i<a.len||i<b.len;i++){ 8 if(a.d[i]<b.d[i]){ 9 //如果不够减 10 a.d[i+1]--; 11 a.d[i] = a.d[i] + 10; 12 } 13 c.d[c.len++] = a.d[i] - b.d[i]; //减法结果为当前位结果 14 15 } 16 while(c.len>=2&&c.d[c.len-1] == 0){ 17 c.len--; 18 } 19 20 return c; 21 }
需要指出的是,使用sub函数前要比较两个数的大小,如果被减数小于减数,需要交换两个变量然后输出负号,再调用sub函数。
3、高精度与低精度的乘法
对乘法的某一步来说,就是以下这样的步骤:
取bign的某位(从低位开始取)与int型整体相乘,再与进位相加,所得结果的个位数作为该位结果,高位部分作为新的进位。
1 bign multi(bign a,int b){ 2 bign c; 3 memset(c.d,0,1000*sizeof(int)); 4 c.len=0; 5 int i; 6 int carry = 0; 7 for(i=0;i<a.len;i++){ 8 int temp = a.d[i]*b+carry; 9 c.d[c.len++] = temp%10; 10 carry = temp/10; 11 } 12 13 while(carry!=0){ 14 //和加法不一样,乘法的进位可能不止一位,因此用while 15 c.d[c.len++] = carry%10; 16 carry = carry/10; 17 } 18 return c; 19 }
4、高精度与低精度的除法
归纳其中某一步的步骤:上一步的余数乘以10加上该步的位,得到该步临时的被除数,将其与被除数比较;如果不够除,则该位商为0;
如果够除,则商为对应的商,余数即为对应的余数。最后注意除法后高位可能有多余的0,要去除它们,但也要保证结果至少有一位数。
1 bign divide(bign a,int b){ 2 bign c; 3 memset(c.d,0,1000*sizeof(int)); 4 5 c.len = a.len; //被除数的每一位和商的每一位是一一对应的,因此先令长度相等 6 int i,r = 0; 7 for(i=a.len-1;i>=0;i--){ 8 r = r*10+a.d[i]; //和上一位遗留的余数结合 9 10 if(r<b){ 11 //不够除 12 c.d[i] = 0; 13 }else{ 14 c.d[i] = r/b; 15 r = r%b; 16 } 17 18 } 19 while(c.len>=2&&c.d[c.len-1] == 0){ 20 c.len--; 21 } 22 return c; 23 }