大整數,顧名思義就是特別大的整數。
一台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);
}
}
小弟不才,以上是本人根據大整數的處理思路寫出來的代碼,其中需要優化的地方很多,需要不斷修改。
