大整数,顾名思义就是特别大的整数。
一台64位的机器最大能表示的数字是2的64次方减一:
18446744073709551615
java语言中所能表示的整数(int)最小为-2147483648
public class test { public static void main(String[] args) { System.out.println(Integer.MIN_VALUE); } }
最大为
2147483647
public class test { public static void main(String[] args) { System.out.println(Integer.MAX_VALUE); } }
而long所能表示的最大整数为
public class test { public static void main(String[] args) { System.out.println(Long.MAX_VALUE); } }
9223372036854775807
最小为
public class test { public static void main(String[] args) { System.out.println(Long.MIN_VALUE); } }
-9223372036854775808
如果超出了这些范围,就会报 out of range的错,所以java提供了BigInteger这个类来进行大整数的运算。其实实现BigInteger的原理也并不困难。下面是加法,减法已经乘法的实现。
类里面需要一个bignum属性
public class MyBigInteger {
String bignum;
public MyBigInteger(String bigone) {
this.bignum = bigone;
}
加法:根据老师课上得指导,大概思路很简单,就是把大整数分成好几个段(每四个数字一段),然后对应的数字段进行加法。
例如12345678987654321与8765432123456789相加。那么将这两个数字分成
1 2345 6789 8765 4321
8765 4321 2345 6789 对应的和则为
2 1111 1111 1111 1110
(分组的主要思路是从后往前,每四个数字一组,到最前面的一组若不够四位数,则自成一组)
// 分块,在加法和减法里面用到 private String[] getGroup(String number) { int length = (int) Math.ceil(number.length() / 4.0); String[] group = new String[length]; for (int i = length; i > 0; i--) {// 注意,substring是从前往后截取字符,不是,从后往前截取字符 if (number.length() - (length - i) * 4 - 4 < 0) { group[length - i] = number.substring(0, number.length() - (length - i) * 4); } else { group[length - i] = number.substring(number.length() - (length - i) * 4 - 4, number.length() - (length - i) * 4); } } return group; }
其中,需要考虑到块与块之间的进位问题。因为每个对应的块相加,可以使用Integer.parseInt(...)转化成数字后相加,但是相应的块相加后可能会超出9999,这样就要向前面的块进位。这就要设置一个属性boolean[] fillThousand 。来判断是否需要进位,这个数组的长度应该比两个数分块后较长的那个块的长度多一(如上面两个数字分块后,块的长度分别是5和4,所以fillThousand的长度应该是6.)
如果,某一个对应块想加大于9999,那就把这个数减去10000(或者转化为字符串后从第二个字符开始截取),并且把前一个块的fillThousand属性变成true。
如果某一个块对于的fillThousand属性为true,则把这个对应块的和加1.。如果,最前面的那个块的fillThousand属性为true(这就是为什么把ffillThousand长度设置为6),则将总结果前面加一个”1“。
(此处,我是将长度较长的数当作第一个数字,注意,只是长度,在减法里,我会将绝对值较大的数当成第一个数)
private String addPN(String bigtwo) { // 暂时无考虑负数 int a1 = (int) Math.ceil(bignum.length() / 4.0); int a2 = (int) Math.ceil(bigtwo.length() / 4.0); String[] group1 = new String[a1]; String[] group2 = new String[a2]; group1 = getGroup(bignum); group2 = getGroup(bigtwo); if (bigtwo.length() > bignum.length()) {// 将长的放到a1上 String[] temp = group1; group1 = group2; group2 = temp; a1 = group1.length; a2 = group2.length; } String addAll = ""; boolean[] fillThousand = new boolean[a1 + 1];// 每一块数字是否需要进位 for (int i = 0; i < a1; i++) { if (i <= a2 - 1) { Integer i1 = Integer.parseInt(group1[i]); Integer i2 = Integer.parseInt(group2[i]); Integer iall = i1 + i2; if (fillThousand[i]) { iall += 1; } if (iall > 9999 && i != a2 - 1) { // iall=Integer.parseInt(iall.toString().substring(1,5));如是10000,则此方法不行。。0000=0 String subIall = iall.toString().substring(1, 5); iall = Integer.parseInt(subIall); fillThousand[i + 1] = true; } if (iall.toString().length() == 3 && i != a2 - 1) { addAll = "0" + iall + addAll; } else if (iall.toString().length() == 2 && i != a2 - 1) { addAll = "00" + iall + addAll; } else if (iall.toString().length() == 4 && i != a2 - 1) { addAll = iall + addAll; } else if (iall.toString().length() == 1 && i != a2 - 1) { addAll = "000" + iall + addAll; } if (i == a2 - 1) { if (iall > 9999) { // iall=Integer.parseInt(iall.toString().substring(1,5));如是10000,则此方法不行。。0000=0 String subIall = iall.toString().substring(1, 5); iall = Integer.parseInt(subIall); fillThousand[i + 1] = true; } if (iall.toString().length() == 3) { addAll = "0" + iall + addAll; } else if (iall.toString().length() == 2) { addAll = "00" + iall + addAll; } else if (iall.toString().length() == 1) { addAll = "000" + iall + addAll;// 保证最前面的数字不会多加0 } else if (iall.toString().length() == 4) { addAll = iall + addAll; } else if (a1 == a2) { addAll = iall + addAll; } } // 如果最前面的一块相加超过1000 if (fillThousand[a1]) { addAll = "1" + addAll; } } else { Integer iall = Integer.parseInt(group1[i]); if (fillThousand[i]) { iall += 1; } if (i == a1 - 1) { addAll = iall + addAll; } if (iall.toString().length() == 3 && i != a1 - 1) { addAll = "0" + iall + addAll; } else if (iall.toString().length() == 2 && i != a1 - 1) { addAll = "00" + iall + addAll; } else if (iall.toString().length() == 1 && i != a1 - 1) {// iall是否超过1000 addAll = "000" + iall + addAll;// 保证最前面的数字不会多加0 } else if (iall.toString().length() == 1 && iall == 0 && i == a1 - 1) { addAll = addAll;// 保证最前面的数字不会多加0 } else if (iall.toString().length() == 4 && i != a1 - 1) { addAll = iall + addAll; } // 如果最前面的一块相加超过1000 if (fillThousand[a1]) { addAll = "1" + addAll; } } } // 若不进行此步,则会出现000000000001这样的情况 if (addAll != "") { if (addAll.charAt(0) == ‘0‘) { int a = 0; while (Integer.parseInt(addAll.substring(a, a + 1)) == 0) { a += 1; if (a == addAll.length()) { break; } } addAll = addAll.substring(a, addAll.length()); if (a == addAll.length()) { addAll = "0"; } } } else { addAll = "0"; } return addAll; }
以上是,两个正整数进行加法运算时的代码,当然加法里面还会有负数相加。所以结合后来的,两个正整数的减法。可以得出加法的完整算法
public MyBigInteger add(MyBigInteger bigtwo) { String returnNum=null; if (!(this.bignum.charAt(0) == ‘-‘) && !(bigtwo.bignum.charAt(0) == ‘-‘)) { returnNum = addPN(bigtwo.bignum); } else if (this.bignum.charAt(0) == ‘-‘ && !(bigtwo.bignum.charAt(0) == ‘-‘)) { bignum = bignum.substring(1, bignum.length()); if (substractPN(bigtwo.bignum).charAt(0) == ‘-‘) { returnNum = substractPN(bigtwo.bignum).substring(1, substractPN(bigtwo.bignum).length()); } else { returnNum = "-" + substractPN(bigtwo.bignum); } } else if (!(this.bignum.charAt(0) == ‘-‘) && bigtwo.bignum.charAt(0) == ‘-‘) { bigtwo.bignum = bigtwo.bignum.substring(1, bigtwo.bignum.length()); returnNum = substractPN(bigtwo.bignum); } else// 两个都是负数 { bignum = bignum.substring(1, bignum.length()); bigtwo.bignum = bigtwo.bignum.substring(1, bigtwo.bignum.length()); returnNum = "-" + addPN(bigtwo.bignum); } return new MyBigInteger(returnNum); }
减法:
减法的算法比加法的算法要复杂一些,因为减法不仅要考虑借位还要考虑正负。
还是先进行两个整数的减法运算
首先还是分组.......
然后将大的数当成被减数
减法里面要设置两个大属性,一个是boolean reverse,这个意味着,如果是第一个数字小于第二个数字,那就交换两个的位置,然后在结果前面加上”-“。
private String substractPN(String bigtwo) { // 暂时无考虑负数 int a1 = (int) Math.ceil(bignum.length() / 4.0); int a2 = (int) Math.ceil(bigtwo.length() / 4.0); String[] group1 = new String[a1]; String[] group2 = new String[a2]; group1 = getGroup(bignum); group2 = getGroup(bigtwo); boolean reverse = false;// 判断是否是一个小数字减去一个大数字,若是,则交换两个的位置,并在最后加一个负号 boolean oneMoreTwo = true;// 位数相同,比较大小 if (bigtwo.length() == bignum.length()) { // 如果两个数长度相等,比较前两段数字的大小 if (Integer.parseInt(group2[a1 - 1]) > Integer.parseInt(group1[a1 - 1])) { oneMoreTwo = false; } if ((Integer.parseInt(group2[a1 - 1]) == Integer.parseInt(group1[a1 - 1])) && (Integer.parseInt(group2[a1 - 2]) > Integer.parseInt(group1[a1 - 2]))) { oneMoreTwo = false; } } if (bigtwo.length() > bignum.length() || !oneMoreTwo) {// 将长的数放到a1上 String[] temp = group1; group1 = group2; group2 = temp; a1 = group1.length; a2 = group2.length; reverse = true; } //。。。。。。。。。。。。 //。。。。。。。。。。。。 //。。。。。。。。。。。。 //这是最后的两行,中间省略若干代码 if (reverse) { substractAll = "-" + substractAll; }
另外一个boolean[] borrowone,是否借位,和加法的fillThousand属性类似。如果一个对应块相减后结果小于0,那就把这个结果加10000,然后相前一个块借1。然后将前一个块的borrowone设置为true。
若一个块的borrowone属性为true则将这个块的结果减1.
boolean[] borrowone = new boolean[a1 + 1];// 判断是否需要借位 String substractAll = ""; for (int i = 0; i < a1; i++) { if (i <= a2 - 1) { Integer i1 = Integer.parseInt(group1[i]); Integer i2 = Integer.parseInt(group2[i]); Integer isubstract = i1 - i2;// 处理isubstract是0000的情况 if (borrowone[i]) { isubstract -= 1; } if (isubstract < 0) { isubstract = isubstract + 10000;// 判断位数 borrowone[i + 1] = true; if (isubstract > 0 && isubstract.toString().length() == 3) { substractAll = "0" + isubstract + substractAll; } else if (isubstract > 0 && isubstract.toString().length() == 2) { substractAll = "00" + isubstract + substractAll; } else if (isubstract > 0 && isubstract.toString().length() == 1) { substractAll = "000" + isubstract + substractAll; } else if (isubstract > 0 && isubstract.toString().length() == 4) { substractAll = isubstract + substractAll; } else if (isubstract == 0) { substractAll = "0000" + substractAll; } } else if (isubstract > 0 && isubstract.toString().length() == 3) { substractAll = "0" + isubstract + substractAll; } else if (isubstract > 0 && isubstract.toString().length() == 2) { substractAll = "00" + isubstract + substractAll; } else if (isubstract > 0 && isubstract.toString().length() == 1) { substractAll = "000" + isubstract + substractAll; } else if (isubstract > 0 && isubstract.toString().length() == 4) { substractAll = isubstract + substractAll; } else if (isubstract == 0) { substractAll = "0000" + substractAll; } } else { Integer isubstract = Integer.parseInt(group1[i]); if (borrowone[i]) { isubstract -= 1; } if (i == a1 - 1) { substractAll = isubstract + substractAll; } if (isubstract > 0 && isubstract.toString().length() == 3 && i != a1 - 1) { substractAll = "0" + isubstract + substractAll; } else if (isubstract > 0 && isubstract.toString().length() == 2 && i != a1 - 1) { substractAll = "00" + isubstract + substractAll; } else if (isubstract > 0 && isubstract.toString().length() == 1 && i != a1 - 1) { substractAll = "000" + isubstract + substractAll; } else if (isubstract > 0 && isubstract.toString().length() == 4 && i != a1 - 1) { substractAll = isubstract + substractAll; } else if (isubstract == 0) { substractAll = "0000" + substractAll; } } }
当然,减法,还要处理一下000001这类情况
// 若不进行此步,则会出现000000000001这样的情况
if (Integer.parseInt(substractAll.substring(0, 1)) == 0) {
int a = 0;
while (Integer.parseInt(substractAll.substring(a, a + 1)) == 0) {
a += 1;
}
substractAll = substractAll.substring(a, substractAll.length());
}
以上是正数减法的实现,结合加法,则可以实现完整的减法
public MyBigInteger substract(MyBigInteger bigtwo) { // 只能用equals不能用== String returnNum=null; if (!(this.bignum.charAt(0) == ‘-‘) && !(bigtwo.bignum.charAt(0) == ‘-‘)) { returnNum = substractPN(bigtwo.bignum); } else if (this.bignum.charAt(0) == ‘-‘ && !(bigtwo.bignum.charAt(0) == ‘-‘)) { bignum = bignum.substring(1, bignum.length()); returnNum = "-" + addPN(bigtwo.bignum); } else if (!(this.bignum.charAt(0) == ‘-‘) && bigtwo.bignum.charAt(0) == ‘-‘) { bigtwo.bignum = bigtwo.bignum.substring(1, bigtwo.bignum.length()); returnNum = addPN(bigtwo.bignum); } else {// 两个都是负数 bignum = bignum.substring(1, bignum.length()); bigtwo.bignum = bigtwo.bignum.substring(1, bigtwo.bignum.length()); if (substractPN(bigtwo.bignum).charAt(0) == ‘-‘) { returnNum = substractPN(bigtwo.bignum).substring(1, substractPN(bigtwo.bignum).length()); } else { returnNum = "-" + substractPN(bigtwo.bignum); } } return new MyBigInteger(returnNum); }
乘法:
乘法的算法思想比较简单。我采用的逐位相乘的思路,即两个数下标和相同的数字相乘之后相加。然后最后的和作为结果对对应的位数。即∑Ai(A的i位)*Bz-i(B的z-i位) = Cz
若Cz大于十,则保留其个位数,并且向Cz+1进位,进的位数为Cz十位以上的数字,例如Cz=123,则向前进十二位。
public MyBigInteger multiply(MyBigInteger bigtwo) { String returnNum=null; boolean positive = false; if ((bigtwo.bignum.charAt(0) == ‘-‘ && this.bignum.charAt(0) == ‘-‘) || (!(bigtwo.bignum.charAt(0) == ‘-‘) && !(this.bignum.charAt(0) == ‘-‘))) { positive = true; } if (bigtwo.bignum.charAt(0) == ‘-‘) { bigtwo.bignum = bigtwo.bignum.substring(1); } if (this.bignum.charAt(0) == ‘-‘) { this.bignum =this.bignum.substring(1); } int a = this.bignum.length(); int b = bigtwo.bignum.length(); String[] s1 = new String[a]; String[] s2 = new String[b]; int[] mulAll = new int[a + b - 1]; for (int i = 0; i < a; i++) { s1[a - i - 1] = this.bignum.substring(i, i + 1); } for (int i = 0; i < b; i++) { s2[b - i - 1] = bigtwo.bignum.substring(i, i + 1); } if (a < b) { int temp = a; a = b; b = temp; String[] stemp = s1; s1 = s2; s2 = stemp; } for (int i = 0; i < a; i++) { for (int j = 0; j < b; j++) { mulAll[i + j] +=Integer.parseInt(s1[i]) * Integer.parseInt(s2[j]); } } for (int i = 0; i < mulAll.length - 1; i++) { if (mulAll[i] > 9) { while (mulAll[i] > 9) { mulAll[i] -=10; mulAll[i + 1] += 1; } } } returnNum = ""; for (int i = mulAll.length - 1; i >= 0; i--) { returnNum = returnNum + mulAll[i]; } if (positive) { return new MyBigInteger2(returnNum); } else { returnNum = "-" + returnNum; return new MyBigInteger(returnNum); } }
小弟不才,以上是本人根据大整数的处理思路写出来的代码,其中需要优化的地方很多,需要不断修改。