1. 程式人生 > >基於Java的大整數運算的實現(加法,減法,乘法)學習筆記

基於Java的大整數運算的實現(加法,減法,乘法)學習筆記

-1 urn 相加 his add oid one 我會 後來

大整數,顧名思義就是特別大的整數。

一臺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的大整數運算的實現(加法,減法,乘法)學習筆記