java.math.BigInteger类

ava中long类型可以表示 -9,223,372,036,854,775,808(即-2^64)到9,223,372,036,854,775,807(即2^64-1)范围内的整数。有的时候我们希望能够处理在此范围之外的整数。
为此,我们设计了一个BigInteger类。它可以支持大整数的加、减、乘操作。请根据提供的代码框架,完成整个程序。

> 注:
> 1) 请仔细阅读代码中的注释,并在标有// YOU FILL THIS IN 的位置添加你的代码。你可以添加自己的方法、变量,但是请不要修改已有的代码。
> 2) 程序中的main函数只是一个示例测试。在批改作业时,我们会用更多的用例来测试你的程序。所以请保证你的程序的正确性。
> 3) 我们在测试你的程序时,将使用合法的输入值,因此你无需考虑输入不合法的情况。输入值可能有两种形式:"343839939" 或者
  "-394939399390343",输入的数字位数不固定,但一定是合法的整数。
> 4)  不允许使用java.math包。
> 5) 请将你的项目打包成rar或者zip格式提交。请注意提交的格式:打开你的压缩包,应该可以看到BigInteger文件夹,这个文件夹里面包含了src和bin两个文件夹。
  如果提交作业格式错误,将不能通过测试,没有得分。

给出的代码是这样子的:
Java代码   收藏代码
  1. public class BigInteger {   
  2.     
  3.     // The sign of this integer - true for a positive number, and false   
  4.     // otherwise   
  5.     private boolean sign = true;   
  6.     
  7.     // digits[0] is the most significant digit of the integer, and   
  8.     // the last element of this array is the least significant digit.   
  9.     // For example, if we have a BigInteger of value 34, then   
  10.     // digits[0] = 3 and digits[1] = 4.   
  11.     private byte[] digits;   
  12.     
  13.     public BigInteger() {   
  14.         this.digits = new byte[1];   
  15.         this.digits[0] = 0;   
  16.     }   
  17.     
  18.     public BigInteger(byte[] digits) {   
  19.         this.digits = digits;   
  20.     }   
  21.     
  22.     /**  
  23.      * Initializes a <code>BigInteger</code> according to a string. The form of  
  24.      * <code>numberStr</code> is a string consisting of all digits ranging from  
  25.      * 0 to 9, following an OPTIONAL minus symbol (i.e., "-"). For example,  
  26.      * "1234567891234567" and "-17788399934334388347734" are both valid.  
  27.      *   
  28.      * @param numberStr  
  29.      *            a number expressed as a string  
  30.      */  
  31.     public BigInteger(String numberStr) {   
  32.         // YOU FILL THIS IN   
  33.         // Note: You should parse the string and initialize the "digits" array   
  34.         // properly.   
  35.         // You may also need to set the "sign" variable to a correct value.   
  36.     }   
  37.     
  38.     /**  
  39.      * Performs addition.  
  40.      *   
  41.      * @param another  
  42.      *            another <code>BigInteger</code> object  
  43.      * @return this+another  
  44.      */  
  45.     public BigInteger add(BigInteger another) {   
  46.         // YOU FILL THIS IN   
  47.     }   
  48.     
  49.     /**  
  50.      * Performs addition.  
  51.      *   
  52.      * @param num  
  53.      *            an integer  
  54.      * @return this+num  
  55.      */  
  56.     public BigInteger add(int num) {   
  57.         // YOU FILL THIS IN   
  58.     }   
  59.     
  60.     /**  
  61.      * Performs subtraction.  
  62.      *   
  63.      * @param another  
  64.      *            another <code>BigInteger</code> object  
  65.      * @return this-another  
  66.      */  
  67.     public BigInteger subtract(BigInteger another) {   
  68.         // YOU FILL THIS IN   
  69.     }   
  70.     
  71.     /**  
  72.      * Performs subtraction.  
  73.      *   
  74.      * @param num  
  75.      *            an integer  
  76.      * @return this-num  
  77.      */  
  78.     public BigInteger subtract(int num) {   
  79.         // YOU FILL THIS IN   
  80.     }   
  81.     
  82.     /**  
  83.      * Performs multiplication.  
  84.      *   
  85.      * @param another  
  86.      *            another <code>BigInteger</code> object  
  87.      * @return this*another  
  88.      */  
  89.     public BigInteger multiply(BigInteger another) {   
  90.         // YOU FILL THIS IN   
  91.     }   
  92.     
  93.     /**  
  94.      * Performs multiplication.  
  95.      *   
  96.      * @param num  
  97.      *            an integer  
  98.      * @return this*num  
  99.      */  
  100.     public BigInteger multiply(int num) {   
  101.         // YOU FILL THIS IN   
  102.     }   
  103.     
  104.     public String toString() {   
  105.         StringBuffer buf = new StringBuffer();   
  106.         if (!sign) {   
  107.             buf.append("-");   
  108.         }   
  109.         for (byte d : digits) {   
  110.             buf.append(d);   
  111.         }   
  112.         return buf.toString();   
  113.     }   
  114.     
  115.     public static void main(String[] args) {   
  116.         BigInteger i1 = new BigInteger("999999999999999999");   
  117.         BigInteger i2 = i1.add(1);   
  118.         System.out.println(i2); // the output should be 1000000000000000000   
  119.         BigInteger i3 = i2.multiply(i1);   
  120.         System.out.println(i3); // expected: 999999999999999999000000000000000000   
  121.         System.out.println(i3.subtract(-3)); // expected: 999999999999999999000000000000000003   
  122.     }   
  123. }  

其实,大家不要被这个看似很难的题目吓到,想想小学时我们刚开始学加法和乘法的时候,老师都教我们怎么算?竖式运算,对!由于BigInteger把每一位数都存在数组中,也为竖式运算提供了方便。
我们需要写的方法是:
public BigInteger(String numberStr);
public BigInteger add(BigInteger another);
public BigInteger add(int num);
public BigInteger subtract(BigInteger another);
public BigInteger subtract(int num);
public BigInteger multiply(BigInteger another);
public BigInteger multiply(int num);
3个小时,如果真的每个方法都写的话,估计很难做完。化归思想大家都学过,我们来运用一下:
Java代码   收藏代码
  1. public BigInteger(int integer){   
  2.     this(String.valueOf(integer));   
  3. }   
  4.    
  5.     public BigInteger add(int num) {   
  6.     return this.add(new BigInteger(num));   
  7. }   
  8.    
  9. public BigInteger subtract(BigInteger another) {   
  10.     return this.add(another.negate());   
  11. }   
  12.    
  13. public BigInteger subtract(int num) {   
  14.     return this.subtract(new BigInteger(num));   
  15. }   
  16.    
  17. public BigInteger multiply(int num) {   
  18.     return this.multiply(new BigInteger(num));   
  19. }  


这里的negate方法是新加的,就是返回一个相反数。减法就是加上一个数的相反数,对吧?(注意,为了防止可能的副作用,这里使用了deep copy)
Java代码   收藏代码
  1. public BigInteger negate(){   
  2.     BigInteger bi = new BigInteger();   
  3.     byte[] digitsCopy = new byte[this.digits.length];   
  4.     for(int i = 0;i < this.digits.length;i++){   
  5.         digitsCopy[i] = this.digits[i];   
  6.     }   
  7.     bi.sign = !this.sign;   
  8.     bi.digits = digitsCopy;   
  9.     return bi;   
  10. }  

于是,我们需要写的方法减少到了3个:
public BigInteger(String numberStr);
public BigInteger add(BigInteger another);
public BigInteger multiply(BigInteger another);
第一个方法不难,只要先判断第一个字符是不是'-',然后再把不是负号的部分加到digits数组中就行。(由于明确了输入格式肯定是正确的,这里不考虑输入格式的问题):
Java代码   收藏代码
  1. public BigInteger(String numberStr) {   
  2.     // YOU FILL THIS IN   
  3.     // Note: You should parse the string and initialize the "digits" array   
  4.     // properly.   
  5.     // You may also need to set the "sign" variable to a correct value.   
  6.     if(numberStr.charAt(0) == '-'){   
  7.         sign = false;   
  8.         StringBuilder sb = new StringBuilder(numberStr);   
  9.         sb.deleteCharAt(0);   
  10.         numberStr = new String(sb);   
  11.     }else{   
  12.         sign = true;   
  13.     }   
  14.    
  15.     digits = new byte[numberStr.length()];   
  16.     for(int i = 0;i < numberStr.length();i++){   
  17.         switch(numberStr.charAt(i)){   
  18.         case '0': digits[i] = 0;break;   
  19.         case '1': digits[i] = 1;break;   
  20.         case '2': digits[i] = 2;break;   
  21.         case '3': digits[i] = 3;break;   
  22.         case '4': digits[i] = 4;break;   
  23.         case '5': digits[i] = 5;break;   
  24.         case '6': digits[i] = 6;break;   
  25.         case '7': digits[i] = 7;break;   
  26.         case '8': digits[i] = 8;break;   
  27.         case '9': digits[i] = 9;break;   
  28.         }   
  29.     }   
  30. }  

然后来看public BigInteger add(BigInteger another)方法,我们要考虑什么?首先是符号,如果两数同号,那好办,和肯定是和两数符号相同的;若异号,那么我们就要看两数绝对值的大小了,和与绝对值大的数同号。
怎样判断绝对值呢?首先看位数,位数大的绝对值肯定大。位数相同,则从首位开始比较,只要有一位不同,不同位置的数字大的大;如果每一位都相同,那么我们得到的就是0,省事很多。
然后我们考虑具体的加减,同号相加(其实是绝对值相加),要考虑进位问题,而产生的和的位数最多比绝对值大的数多一位;异号相加,其实是绝对值相减,要考虑借位问题,而得到的差位数不大于绝对值大的数。
这里要特别注意的是,竖式计算的是从最末尾开始的,而我们的数组首位存储的是最高位,第二位是第二高位,一次类推;故我们这里用的循环大多同平日写的循环有些不同:for(int i = 1;i <= digits.length;i++)。
梳理好以上思路,add方法写法如下:
Java代码   收藏代码
  1. public BigInteger add(BigInteger another) {   
  2.     // YOU FILL THIS IN   
  3.     BigInteger sum = new BigInteger();   
  4.     if(this.sign == another.sign){   
  5.         //the signs of both are equal   
  6.         int length1 = this.digits.length;   
  7.         int length2 = another.digits.length;   
  8.         int biggerLength = Math.max(length1, length2);   
  9.         byte[] temp = new byte[biggerLength];   
  10.         byte carry = 0;   
  11.             
  12.         for(int i = 1;i <= biggerLength;i++){   
  13.             byte i1 = (length1 - i < 0)?0:this.digits[length1 - i];   
  14.             byte i2 = (length2 - i < 0)?0:another.digits[length2 -i];   
  15.             int s = i1 + i2 + carry;   
  16.             if(s < 10){   
  17.                 temp[biggerLength - i] = (byte)s;   
  18.                 carry = 0;   
  19.             }else{   
  20.                 temp[biggerLength - i] = (byte)(s - 10);   
  21.                 carry = 1;   
  22.             }   
  23.         }   
  24.             
  25.         if(carry == 0){   
  26.             sum.digits = temp;   
  27.         }else{   
  28.             sum.digits = new byte[biggerLength + 1];   
  29.             sum.digits[0] = carry;   
  30.             for(int i = 0;i < biggerLength;i++){   
  31.                 sum.digits[i + 1] = temp[i];   
  32.             }   
  33.         }   
  34.             
  35.         sum.sign = this.sign;   
  36.     }else{   
  37.         //the signs differ   
  38.         boolean isAbsoluteEqual = false;//the default value is false   
  39.         boolean isThisAbsoluteBigger = false;// the default value is false   
  40.             
  41.         if(this.digits.length > another.digits.length){   
  42.             isThisAbsoluteBigger = true;   
  43.         }else if(this.digits.length == another.digits.length){   
  44.             isAbsoluteEqual = true;   
  45.             for(int i = 0;i < this.digits.length;i++){   
  46.                 if(this.digits[i] != another.digits[i]){   
  47.                     if(this.digits[i] > another.digits[i]){   
  48.                         isThisAbsoluteBigger = true;   
  49.                     }   
  50.                     isAbsoluteEqual = false;   
  51.                     break;   
  52.                 }   
  53.             }   
  54.         }   
  55.             
  56.         //if isAbsoluteEqual is true, the sum should be 0, which is just the default value   
  57.         if(!isAbsoluteEqual){   
  58.             byte[] temp;   
  59.             byte[] bigger;   
  60.             byte[] smaller;   
  61.                 
  62.             if(isThisAbsoluteBigger){   
  63.                 sum.sign = this.sign;   
  64.                 temp = new byte[this.digits.length];   
  65.                 bigger = this.digits;   
  66.                 smaller = another.digits;   
  67.             }else{   
  68.                 sum.sign = another.sign;   
  69.                 temp = new byte[another.digits.length];   
  70.                 bigger = another.digits;   
  71.                 smaller = this.digits;   
  72.             }   
  73.                 
  74.             boolean borrow = false;   
  75.             for(int index = 1;index <= bigger.length;index++){   
  76.                 byte biggerDigit = bigger[bigger.length - index];   
  77.                 biggerDigit = (byte) ((borrow)?(biggerDigit - 1):biggerDigit);   
  78.                 byte smallerDigit = (smaller.length - index < 0)?0:smaller[smaller.length - index];   
  79.                 int s = biggerDigit - smallerDigit;   
  80.                 if(s < 0){   
  81.                     borrow = true;   
  82.                     s += 10;   
  83.                 }else{   
  84.                     borrow = false;   
  85.                 }   
  86.                 temp[temp.length - index] = (byte)s;   
  87.             }   
  88.                 
  89.             int zeroCount = 0;   
  90.             for(int i = 0;i < temp.length;i++){   
  91.                 if(temp[i] == 0){   
  92.                     zeroCount++;   
  93.                 }else{   
  94.                     break;   
  95.                 }   
  96.             }   
  97.             sum.digits = new byte[temp.length - zeroCount];   
  98.             for(int i = 0;i < sum.digits.length;i++){   
  99.                 sum.digits[i] = temp[zeroCount + i];   
  100.             }   
  101.         }   
  102.     }   
  103.     return sum;   
  104. }  
  105.    


最后就是乘法了,其实还是竖式计算,就是稍微麻烦了一点(暂时还没找到更好的解法,3个小时,咱就不考虑什么算法优化了)。第一还是先考虑符号,这个比加法简单,要是同号,商为正,要是异号,那么商为负。
第二是具体的竖式计算怎么做,先看一个例子:
Java代码   收藏代码
  1.           
  2.         1 2 3 4  
  3.        x  9 3 4  
  4. ----------------   
  5.         4 9 3 6  
  6.       3 7 0 2  
  7. 1 1 1 0 6  
  8. ----------------   
  9.   1 1 5 2 5 5 6  


规律是什么?1. 大数在上,小数在下; 2. 大数乘以小数的每一位,分别得到商; 3.最后把各个商按位相加。 听上去挺简单,不是吗?实现1,和加法里面的循环类似,从低位开始相乘,还要考虑进位。2中的商我们可以保存在一个二维数组中,数组第一维的大小是较小数的位数,第二维是较大数的位数+1,为什么有个+1?看看上面的第三个商,最多有可能比大数多一位。 而3呢?位数的便宜似乎比较难实现,但是想想我们上次做过的WordPuzzle,如果是个矩阵呢?其实上面的几个商,按照矩阵排列就是:
Java代码   收藏代码
  1. 0 4 9 3 6  
  2. 0 3 7 0 2  
  3. 1 1 1 0 6  

对应的加法是沿着主对角线方向的!有点感觉了是吧,而这个矩阵就是我们刚刚做的那个二维数组!
乘法实现如下:
Java代码   收藏代码
  1. public BigInteger multiply(BigInteger another) {   
  2.     // YOU FILL THIS IN   
  3.     BigInteger product = new BigInteger();   
  4.         
  5.     if(this.sign == another.sign){   
  6.         product.sign = true;   
  7.     }else{   
  8.         product.sign = false;   
  9.     }   
  10.         
  11.     int biggerLength;   
  12.     int smallerLength;   
  13.     byte[] bigger;   
  14.     byte[] smaller;   
  15.     byte[][] tempProducts;   
  16.         
  17.     if(this.digits.length >= another.digits.length){   
  18.         biggerLength = this.digits.length;   
  19.         smallerLength = another.digits.length;   
  20.         bigger = this.digits;   
  21.         smaller = another.digits;   
  22.     }else{   
  23.         biggerLength = another.digits.length;   
  24.         smallerLength = this.digits.length;   
  25.         bigger = another.digits;   
  26.         smaller = this.digits;   
  27.     }   
  28.     tempProducts = new byte[smallerLength][];   
  29.         
  30.     for(int i = 1;i <= smallerLength;i++){   
  31.         byte[] temp = new byte[biggerLength + 1];//make plenty of space to avoid overflow   
  32.         byte carry = 0;   
  33.         byte m1 = smaller[smallerLength - i];   
  34.             
  35.         for(int j = 1;j <= biggerLength;j++){   
  36.             byte m2 = bigger[biggerLength - j];   
  37.             int tempProduct = m1 * m2 + carry;   
  38.             temp[biggerLength + 1 - j] = (byte)(tempProduct % 10);   
  39.             carry = (byte)(tempProduct / 10);   
  40.         }   
  41.         temp[0] = carry;   
  42.             
  43.         tempProducts[i - 1] = temp;   
  44.     }   
  45.         
  46.     byte[] sum = new byte[smallerLength + biggerLength];   
  47.     byte carry = 0;   
  48.     int count = 1;   
  49.     int row = 0;   
  50.     int column = biggerLength;   
  51.     while(count <= sum.length){   
  52.         int startR = row;   
  53.         int startC = column;   
  54.         int currentSum = 0;   
  55.         while((startR < smallerLength) && (startC < biggerLength + 1)){   
  56.             currentSum += tempProducts[startR][startC];   
  57.             startR++;   
  58.             startC++;   
  59.         }   
  60.         currentSum += carry;   
  61.         if(currentSum < 10){   
  62.             sum[sum.length - count] = (byte)(currentSum);   
  63.             carry = 0;   
  64.         }else{   
  65.             sum[sum.length - count] = (byte)(currentSum % 10);   
  66.             carry = (byte)(currentSum / 10);   
  67.         }   
  68.             
  69.         //System.out.println("processing digit: " + (sum.length - count) + " current digit: " + sum[sum.length - count] + " current carry: " + carry);   
  70.             
  71.         if(column == 0){   
  72.             row++;   
  73.         }else{   
  74.             column--;   
  75.         }   
  76.         count++;   
  77.     }   
  78.         
  79.     int zeroCount = 0;   
  80.     for(int i = 0;i < sum.length;i++){   
  81.         if(sum[i] == 0){   
  82.             zeroCount++;   
  83.         }else{   
  84.             break;   
  85.         }   
  86.     }   
  87.         
  88.     product.digits = new byte[sum.length - zeroCount];   
  89.     for(int i = 0;i < product.digits.length;i++){   
  90.         product.digits[i] = sum[zeroCount + i];   
  91.     }   
  92.         
  93.     return product;   
  94. }  


以上几个方法中的zeroCount和接下来跟着的循环是用来去掉数组前几位不必要的0而设计的。
用java.math包内自带的BigInteger测试了几个相同的实例:

Java代码   收藏代码
  1. public static void main(String[] args) {   
  2.     BigInteger bi = new BigInteger("-123456789").multiply(new BigInteger("1111111111")).multiply(new BigInteger("-222222222")).multiply(new BigInteger("333333333")).multiply(new BigInteger("-444444444")).multiply(new BigInteger("555555555")).multiply(new BigInteger("678987654"));   
  3.     System.out.println(bi);   
  4.     BigInteger bi2 = new BigInteger("123456789").multiply(new BigInteger("-999999999").multiply(new BigInteger("2387423749237")));   
  5.     System.out.println(bi2);   
  6.     System.out.println(bi.subtract(bi2));   
  7.     System.out.println(bi.add(bi2));   
  8. }  


得到的结果:(没两行上面的是java.math包内的BigInteger的结果,下面是我写的BigInteger的结果)

Java代码   收藏代码
  1. -1703513391044005504226057865745939441413078537590685258276720  
  2. -1703513391044005504226057865745939441413078537590685258276720  
  3.     
  4. -294743669768397549929858780007  
  5. -294743669768397549929858780007  
  6.     
  7. -1703513391044005504226057865745644697743310140040755399496713  
  8. -1703513391044005504226057865745644697743310140040755399496713  
  9.     
  10. -1703513391044005504226057865746234185082846935140615117056727  
  11. -1703513391044005504226057865746234185082846935140615117056727 

  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值