基於Java的大整數運算的實現(加法,減法,乘法)學習筆記
大整數,顧名思義就是特別大的整數。
一臺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); } }
小弟不才,以上是本人根據大整數的處理思路寫出來的代碼,其中需要優化的地方很多,需要不斷修改。
基於Java的大整數運算的實現(加法,減法,乘法)學習筆記