关于大数运算(不完整版)

大数问题,基本都是把数先以字符串的形式读入,然后转换为整型数组(个位存在数组0的位置),之后就是模拟手算。由于最近重新翻看白书,觉得LRJ的高精度运算的BIGN类是个好东西,以后比赛的时候可以把这当成模板来用。

 

大数加法:

大数加法,要处理的问题有三个,一个是整型数组的储存问题,还有一个就是进位和本位和的问题以及虽然两数相加完成,但是如果还有进位的话应该继续处理。

在做相加运算的时候应该以位数较多的那个一个数字的长度为标准,比如55 + 550,那么相加的位数一定是3位,不足的因为是真值,当然要补零。

  

bign operator + (const bign& b) const
{
    bign c;
    c.len = 0;
    for(int i = 0, g = 0; g || i < max(len, b.len); i++) {
      int x = g;           //每次进行当前位相加运算时,先看看上一位有没有进位可加上
      if(i < len) x += s[i];
      if(i < b.len) x += b.s[i];
      c.s[c.len++] = x % 10;          //留下本位和//
      g = x / 10;                    //算出进位//
    }
    return c;
  }

代码中需要需要注意的就是,判断条件有两个,一旦两个数都按位相加完成,但是还有一个进位,那么必须要把这个进位加到最高位上去,不然可能能回漏掉最高位。

 

大数乘法:

根据模拟手算的原则就可以知道,乘法运算的时候,部分积相加的时候要错位相加,而且两数想成,那么结果的长度最多为两个数的位数相加

bign operator * (const bign& b) {

bign c; c.len = len + b.len;    //给定结果的位数//

//*以一个数为准,分别把每一位与另一个数的每一位相乘,关键在于成绩的结果存在哪呢?个位为十位为1,一次类推,那么一个数的个位和另一个数的其他位相乘,肯定要存在【0 + i】这个位置上,这样的话,对应的乘积结果存放在对应的位上,就解决了部分积错位相加的问题,至于进位问题先不要管,等到最后集中处理//*/

   for(int i = 0; i < len; i++)        
      for(int j = 0; j < b.len; j++)
        c.s[i+j] += s[i] * b.s[j];
    for(int i = 0; i < c.len-1; i++){
      c.s[i+1] += c.s[i] / 10;       //从第二位开始,加上前面的进位,前面的保留本位和//
      c.s[i] %= 10;
    }
    c.clean();                  //处理运算结果的前导零//
    return c;
  }


 

 

大数减法:

大数减法应该有两种情况,第一种是不需要借位,直接相减,也就是说可以出现负数,另一个就是按照我们平时的习惯,如果本位被减数小于减数,那我们就需要借位。

//此模板只适用于被减数大于减数的情况,因为大数是从个位开始存在数组里面的,比如8 - 88,就为08 - 88,试一试就知道//

 

 bign operator - (const bign& b) 
{
    bign c; c.len = 0;
for(int i = 0, g = 0; i < max(len, b.len); i++) 
{
      int x = s[i] - g;           //之前如果有在此位借位,那么-1//
      if(i < b.len) x -= b.s[i];
      if(x >= 0) g = 0;         //不用借位g = 0;//
      else  if(i + 1 < len)   //如果前面一位允许借,那么才可以,并且数组实现已经清零//
     {
        g = 1;
        x += 10;
      }
      c.s[c.len++] = x;
    }
    c.clean();
    return c;
  }
 

 

大数除法:

大数除法基本也分为两种,一种为大数和大数相除,此时就应该用减法来做,用被除数不断剪掉除数,计算减法的次数,直到结果为负数,那么减法的次数就为商的数值。

另一种是被除数是大数,但是除数是整数,此时还按大数运算的方法显然会超时,比如11111111111111111  / 2. 可想而知,被除数-除数要多少次才能变为负数,这是行不通的。所以,从大数的int型数组中,每次取一位除以除数,将每次的商存入数组,然后对除数取余,不断的乘10加上大数的下一位进行除法。最后在处理前导零的问题

 

i

int operator / (const bign& b)const
{
bign c;
bign yushu;
bign bijiao;
int counts = 0;
c = (*this);
int flag = 0;
bijiao = 0;
while(1)
{
c = c - b;
bijiao.len = c.len;
flag = c >= bijiao ? 0 : 1;
if(flag == 0)
{
counts++;
yushu = c;
}
else
break;
}
return counts;
}
 
 
bign operator / (int& b)const
{
long long int x = 0;  //当除数最大为int的最大值时,那么被除数要比除数还大才行,所有X要为long long //
bign c;
c = 0;
c.len = 0;
for(int i = len - 1; i >= 0; i--)   //从最高位开始模拟手算//
{
x = x * 10 + s[i];                //每次*10再加上下一位//
c.s[c.len++] = x / b;              //将每次的商存下//
x = x % b;                     //保留余数做后续运算//
}
//c.clean();
return c;
}

 

 

PS:除法和乘法不懂的,模拟下手算就全都明白了。大数比较暂时先不写了,这次总结的全都出自于刘汝佳小白书的高精度运算的bign类,有兴趣的可以去看看。另外,大数减法仍有很多缺陷,那个只是针对其中一种比较特殊的情况

http://blog.csdn.net/akof1314/article/details/4412085

这个文档不错,可以看看。

你可能感兴趣的:(关于大数运算(不完整版))