这里分享我自己写的一个大数字的四则运算的实现,乘法是我首先实现的,是参考别人的,加法、减法、除法和比较大小是我自己摸索着写的。
加法、减法和乘法是可以独立写成的,而除法就比较麻烦,需要比较大小、减法和乘法的辅助才能完成。
四则运算的实现方法是我们小学时用的算式法,比较low,胜在比较简单,容易理解容易实现。细节在注释中。
由于我不是计算机专业的,算法与数据结构部分一直是我的软肋。我从别人的csdn blog学到很多好东西,受益匪浅,因此我也分享我的一些学习历程。
由于水平有限,难免有很多bug,我自己写的测试类里面列举了我bug调试的一些例子,有更多的bug调试欢迎提出。
MyBigInt.java
public class MyBigInt {
private char[] numArray;
public MyBigInt(long a) {
String temp = String.valueOf(a);
numArray = temp.toCharArray();
}
public MyBigInt(int a) {
String temp = String.valueOf(a);
numArray = temp.toCharArray();
}
public MyBigInt(String a) {
numArray = a.toCharArray();
}
public MyBigInt(char[] c) {
int blank = 0;
for (int i=0; i < c.length; i++) {
if (c[i] == '\0' || c[i] == '0') {
blank++;
} else {
break;
}
}
if (blank >= c.length) {
numArray = new char[]{'0'};
} else {
numArray = new char[c.length-blank];
copyArray(c, numArray, blank, blank+numArray.length);
}
}
public static MyBigInt valueOf(long a) {
MyBigInt myBigInt = new MyBigInt(a);
return myBigInt;
}
public static MyBigInt valueOf(String a) {
MyBigInt myBigInt = new MyBigInt(a);
return myBigInt;
}
/**
* 比较大数字
* @param bigInt 需要进行比较的大数字
* @return 1 if param is smaller
* 0 if param is equal
* -1 if param is larger
*/
public int compareTo(MyBigInt bigInt) {
int len_this = this.numArray.length;
int len_param = bigInt.numArray.length;
if (len_this > len_param) {
return 1;
} else if (len_this < len_param) {
return -1;
} else {
for (int i=0; i<len_this; i++) {
if (this.numArray[i] > bigInt.numArray[i]) {
return 1;
} else if (this.numArray[i] < bigInt.numArray[i]) {
return -1;
}
}
return 0;
}
}
/**
* 大数字加法,返回一个新的大数字作为结果
* @param bigInt 相加的大数字
* @return 以大数字存储的加法结果
*/
public MyBigInt add(MyBigInt bigInt) {
char[] A = new char[numArray.length];
copyArray(numArray, A);
char[] B = new char[bigInt.getNumArray().length];
copyArray(bigInt.getNumArray(), B);
int m = A.length;
int n = B.length;
//相加的结果位数最多为最长的加数的位数+1,即《=max(m, n)+1
char[] result = new char[Math.max(m, n)+1];
int[] resultInt = new int[Math.max(m, n)+1];
init(result);
init(resultInt);
//倒叙,比较符合我们普通手算的习惯。普通手算从右算并向左进位,对于计算机来说从左向右算,下标比较容易理解
reverseOrder(A, 0, m-1);
reverseOrder(B, 0, n-1);
//保存进位信息
int promoteFlag = 0;
//先做教短的那个数的相加结果,再将较长的数剩下的位数相加完全
for (int i=0; i<=Math.min(n, m)-1; i++) {
int temp = resultInt[i] + (A[i] - 48) + (B[i] - 48) + promoteFlag;
promoteFlag = temp / 10;
resultInt[i] = temp % 10;
}
//再将较长的数剩下的位数相加完全
for (int i=Math.min(n, m); i<=Math.max(n, m)-1; i++) {
int temp = resultInt[i] + ((A.length > B.length ? A[i] : B[i]) - 48) + promoteFlag;
promoteFlag = temp / 10;
resultInt[i] = temp % 10;
}
//如果最后还有进位信息,则放到最右侧作为结果数字的开头
if (promoteFlag > 0) {
resultInt[Math.max(n, m)] += promoteFlag;
}
//将int数字转化为char放入数组中,最右侧开始的0要忽略掉
for (int i = 0; i < resultInt.length; i++) {
if (i == resultInt.length-1 && resultInt[i] == 0) {
continue;
}
result[i] = (char) (resultInt[i] + 48);
}
reverseOrder(result, 0, Math.max(m, n)); //将倒序的结果的数字组合逆向回来
//返回结果的大数字
MyBigInt ret = new MyBigInt(result);
return ret;
}
/**
* 大数字减法,被一个更小的数字相减,目前不支持负数,即被减数》=减数
* @param bigInt 减数
* @return 以大数字存储的加法结果
*/
public MyBigInt subtract(MyBigInt bigInt) {
if (this.compareTo(bigInt) < 0) {
try {
throw new Exception("negative not supported!");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return new MyBigInt(0);
}
}
char[] larger = new char[numArray.length];
copyArray(numArray, larger);
char[] smaller = new char[bigInt.getNumArray().length];
copyArray(bigInt.getNumArray(), smaller);
int m = larger.length;
int n = smaller.length;
//减法结果数字串长度最长不超过被减数
char[] result = new char[m];
int[] resultInt = new int[m];
init(result);
init(resultInt);
//倒叙,比较符合我们普通手算的习惯。普通手算从右算并向左借位,对于计算机来说从左向右算,下标比较容易理解
reverseOrder(larger, 0, m-1);
reverseOrder(smaller, 0, n-1);
//存储借位信息
int borrow = 0;
//从减数的每一位开始计算
for (int i = 0; i < n; i++) {
if (larger[i] > smaller[i]) {//被减数该位大于减数,该位直接相减
resultInt[i] = (larger[i] - 48) - (smaller[i] - 48) - borrow;
borrow = 0;
} else if(larger[i] < smaller[i]) {//被减数该位小于减数,先借位再相减,并记录借位信息
resultInt[i] = (larger[i] - 48 + 10) - (smaller[i] - 48) - borrow;
borrow = 1;
} else {//被减数该位等于减数,若目前没有借位则该位直接相减,否则先借位再相减
if (borrow == 0) {
resultInt[i] = 0;
} else {
resultInt[i] = (larger[i] - 48 + 10) - (smaller[i] - 48) - borrow;
}
}
}
//如果被减数的位数更多,则最后考虑一下是否有借位的情况
if (m > n) {
resultInt[m-1] = larger[m-1] - 48 - borrow;
}
//将int数字转化为char放入数组中,最右侧开始的0要忽略掉
boolean ignoreFlag = true;
for (int i = result.length-1; i >= 0; i--) {
if (resultInt[i] == 0) {
if (ignoreFlag) {
if (i != 0) {
continue;
}
}
} else {
ignoreFlag = false;
}
result[i] = (char) (resultInt[i] + 48);
}
reverseOrder(result, 0, m-1); //将倒序的结果的数字组合逆向回来
MyBigInt ret = new MyBigInt(result);
return ret;
}
/**
* 大数字乘法,返回乘法结果的大数字
* @param bigInt 乘数
* @return 以大数字存储的乘法结果
*/
public MyBigInt multiply(MyBigInt bigInt) {
char[] A = new char[numArray.length];
copyArray(numArray, A);
char[] B = new char[bigInt.getNumArray().length];
copyArray(bigInt.getNumArray(), B);
int m = A.length;
int n = B.length;
//乘法结果的数字串长度最大为被乘数和乘数长度的和,最小为被乘数和乘数长度的和-1
char[] result = new char[m + n];
int[] resultInt = new int[m + n];
init(result);
init(resultInt);
//使用手算算式法,人习惯从右向左算,程序从左向右进位,下标比较容易理解,所以先逆序数字串
reverseOrder(A, 0, m-1);
reverseOrder(B, 0, n-1);
int promoteFlag; //存储进位信息
for(int i=0; i <= n-1; i++) { //遍历乘数的每一位
promoteFlag = 0;
for(int j=0; j <= m-1; j++) { //遍历被乘数的每一位
int temp1 = ((int)A[j] - 48) * ((int)B[i] - 48) + resultInt[i+j] + promoteFlag;
promoteFlag = temp1 / 10;
resultInt[i+j] = (temp1 % 10);
}
resultInt[i+m] += promoteFlag; //每完成乘数的一位计算就先完成一次进位累加
}
//将int数字转化为char放入数组中,最右侧开始的0要忽略掉
for (int i = 0; i < resultInt.length; i++) {
if (i == resultInt.length-1 && resultInt[i] == 0) {
continue;
}
result[i] = (char) (resultInt[i] + 48);
}
reverseOrder(result, 0, m+n-1); //将倒序的结果的数字组合逆向回来
MyBigInt ret = new MyBigInt(result);
return ret;
}
/**
* 大数字除法
* @param bigInt 除数
* @return 以大数字存储的除法结果,忽略小数
*/
public MyBigInt dividedBy(MyBigInt bigInt) {
if (bigInt.toString().equals("0")) {
try {
throw new Exception("divided by zero!");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return new MyBigInt(0);
}
}
if (this.compareTo(bigInt) < 0) { //忽略小数
return new MyBigInt(0);
}
char[] A = new char[numArray.length];
copyArray(numArray, A);
int m = A.length;
int n = bigInt.numArray.length;
//除法的结果数字串最多为被除数减除数+1
char[] result = new char[m - n + 1];
init(result);
boolean addZeroFlag = false; //如果已经找到高位的数字,那么低位如果temp比除数小则加0
char[] temp = new char[n+1]; //temp保存目前尝试的数字
init(temp);
for (int i=0; i<m; i++) { //遍历被除数的每一位,逐位尝试temp能否比除数大
moveLeft(temp); //temp从被除数获取1位并放在最右侧形成新的temp并用来与除数比较
temp[temp.length-1] = A[i];
MyBigInt tempBigInt = new MyBigInt(temp);
if (tempBigInt.compareTo(bigInt) < 0) { //若temp比除数小
if (addZeroFlag) {//若已经找到高位数字,则加0
moveLeft(result);
result[result.length-1] = (char) (0 + 48);
}
//否则说明还未找到高位,需要跳过此位并寻找新的temp进行比较
continue;
} else { //若temp不比除数小
if (!addZeroFlag) { //说明已经找到高位数字,后面如果temp比除数小就记0
addZeroFlag = true;
}
for (int j=9; j>=1; j--) { //在1~9中找到适合的数字使除数乘以这个数字刚好小于temp,那么这个数字就是这位的结果
MyBigInt tempBigInt2 = bigInt.multiply(new MyBigInt(j));
if (tempBigInt2.compareTo(tempBigInt) <= 0) {
moveLeft(result); //将这个位找到的数字放在结果的右侧
result[result.length-1] = (char) (j + 48);
MyBigInt rest = tempBigInt.subtract(tempBigInt2); //相减得到下一轮寻找过程的temp的开头,放到temp中
init(temp);
for (char c : rest.numArray) {
moveLeft(temp);
temp[temp.length-1] = c;
}
break;
}
}
}
}
MyBigInt ret = new MyBigInt(result);
return ret;
}
public char[] getNumArray() {
return numArray;
}
@Override
public String toString() {
return new String(numArray);
}
private void reverseOrder(char[] str, int p, int q) {
char temp;
while(p < q)
{
temp = str[p];
str[p] = str[q];
str[q] = temp;
p ++;
q --;
}
}
private void init(char[] a) {
for (int i = 0; i < a.length; i++) {
a[i] = '\0';
}
}
private void init(int[] a) {
for (int i = 0; i < a.length; i++) {
a[i] = 0;
}
}
private void copyArray(char[] o, char[] t) {
for (int i = 0; i < o.length; i++) {
t[i] = o[i];
}
}
private void copyArray(char[] o, char[] t, int s, int e) {
for (int i = s; i < e; i++) {
t[i-s] = o[i];
}
}
/**
* 向左移动数字串,特别地,最左边的数字会被移到最右边
* @param a 要被移动的数字串
*/
private void moveLeft(char[] a) {
char temp = a[0];
for (int i=1; i<a.length; i++) {
a[i-1] = a[i];
}
a[a.length-1] = temp;
}
}
TestMyBigInt.java
public class TestMyBigInt {
public static void main(String[] args) {
System.out.println("Using MyBigInt:");
MyBigInt A, B;
System.out.println("test compareTo 1st");
A = MyBigInt.valueOf(123456789L);
B = MyBigInt.valueOf(123456789123456789L);
System.out.println("a="+A.toString());
System.out.println("b="+B.toString());
System.out.println("a.c(b) should be "+"-1");
System.out.println("a.c(b)= "+(A.compareTo(B)));
System.out.println("-------------------------------------------");
System.out.println("test compareTo 2nd");
A = MyBigInt.valueOf(1234567890L);
B = MyBigInt.valueOf(123456789L);
System.out.println("a="+A.toString());
System.out.println("b="+B.toString());
System.out.println("a.c(b) should be "+"1");
System.out.println("a.c(b)= "+(A.compareTo(B)));
System.out.println("-------------------------------------------");
System.out.println("test compareTo 3rd");
A = MyBigInt.valueOf(1233567890L);
B = MyBigInt.valueOf(123456789L);
System.out.println("a="+A.toString());
System.out.println("b="+B.toString());
System.out.println("a.c(b) should be "+"1");
System.out.println("a.c(b)= "+(A.compareTo(B)));
System.out.println("-------------------------------------------");
System.out.println("test compareTo 4th");
A = MyBigInt.valueOf(100000001);
B = MyBigInt.valueOf(100000001);
System.out.println("a="+A.toString());
System.out.println("b="+B.toString());
System.out.println("a.c(b) should be "+"0");
System.out.println("a.c(b)= "+(A.compareTo(B)));
System.out.println("-------------------------------------------");
System.out.println("test multiply 1st");
A = MyBigInt.valueOf(123456789L);
B = MyBigInt.valueOf(123456789123456789L);
System.out.println("a="+A.toString());
System.out.println("b="+B.toString());
System.out.println("a*b should be "+"15241578765432099750190521");
System.out.println("a*b= "+(A.multiply(B).toString()));
System.out.println("-------------------------------------------");
System.out.println("test multiply 2nd");
A = MyBigInt.valueOf(1024);
B = MyBigInt.valueOf(768);
System.out.println("a="+A.toString());
System.out.println("b="+B.toString());
System.out.println("a*b should be "+"786432");
System.out.println("a*b= "+(A.multiply(B).toString()));
System.out.println("-------------------------------------------");
System.out.println("test add 1st");
A = MyBigInt.valueOf("7536951");
B = MyBigInt.valueOf("842651397");
System.out.println("a="+A.toString());
System.out.println("b="+B.toString());
System.out.println("a+b should be "+"850188348");
System.out.println("a+b= "+(A.add(B).toString()));
System.out.println("-------------------------------------------");
System.out.println("test add 2nd");
A = MyBigInt.valueOf("99999");
B = MyBigInt.valueOf("99999");
System.out.println("a="+A.toString());
System.out.println("b="+B.toString());
System.out.println("a+b should be "+"199998");
System.out.println("a+b= "+(A.add(B).toString()));
System.out.println("-------------------------------------------");
System.out.println("test subtract 1st");
A = MyBigInt.valueOf("99999");
B = MyBigInt.valueOf("99999");
System.out.println("a="+A.toString());
System.out.println("b="+B.toString());
System.out.println("a-b should be "+"0");
System.out.println("a-b= "+(A.subtract(B).toString()));
System.out.println("-------------------------------------------");
System.out.println("test subtract 2nd");
A = MyBigInt.valueOf("99899");
B = MyBigInt.valueOf("9999");
System.out.println("a="+A.toString());
System.out.println("b="+B.toString());
System.out.println("a-b should be "+"89900");
System.out.println("a-b= "+(A.subtract(B).toString()));
System.out.println("-------------------------------------------");
System.out.println("test subtract 3rd");
A = MyBigInt.valueOf("12345678987654321");
B = MyBigInt.valueOf("1234567887654321");
System.out.println("a="+A.toString());
System.out.println("b="+B.toString());
System.out.println("a-b should be "+"11111111100000000");
System.out.println("a-b= "+(A.subtract(B).toString()));
System.out.println("-------------------------------------------");
System.out.println("test subtract 4th");
A = MyBigInt.valueOf("12345678987654321");
B = MyBigInt.valueOf("2345678987654321");
System.out.println("a="+A.toString());
System.out.println("b="+B.toString());
System.out.println("a-b should be "+"10000000000000000");
System.out.println("a-b= "+(A.subtract(B).toString()));
System.out.println("-------------------------------------------");
System.out.println("test subtract 5th");
A = MyBigInt.valueOf("5555");
B = MyBigInt.valueOf("46555");
System.out.println("a="+A.toString());
System.out.println("b="+B.toString());
System.out.println("a-b should be "+"negative");
System.out.println("a-b= "+(A.subtract(B).toString()));
System.out.println("-------------------------------------------");
System.out.println("test subtract 6th");
A = MyBigInt.valueOf("65555");
B = MyBigInt.valueOf("46555");
System.out.println("a="+A.toString());
System.out.println("b="+B.toString());
System.out.println("a-b should be "+"19000");
System.out.println("a-b= "+(A.subtract(B).toString()));
System.out.println("-------------------------------------------");
System.out.println("test subtract 7th");
A = MyBigInt.valueOf("1000003");
B = MyBigInt.valueOf("999994");
System.out.println("a="+A.toString());
System.out.println("b="+B.toString());
System.out.println("a-b should be "+"9");
System.out.println("a-b= "+(A.subtract(B).toString()));
System.out.println("-------------------------------------------");
System.out.println("test divide 1st");
A = MyBigInt.valueOf("2000");
B = MyBigInt.valueOf("20");
System.out.println("a="+A.toString());
System.out.println("b="+B.toString());
System.out.println("a/b should be "+"100");
System.out.println("a/b= "+(A.dividedBy(B).toString()));
System.out.println("-------------------------------------------");
System.out.println("test divide 2nd");
A = MyBigInt.valueOf("123456789123456789");
B = MyBigInt.valueOf("123456789");
System.out.println("a="+A.toString());
System.out.println("b="+B.toString());
System.out.println("a/b should be "+"1000000001");
System.out.println("a/b= "+(A.dividedBy(B).toString()));
System.out.println("-------------------------------------------");
System.out.println("test divide 3rd");
A = MyBigInt.valueOf("987654321111");
B = MyBigInt.valueOf("123456789");
System.out.println("a="+A.toString());
System.out.println("b="+B.toString());
System.out.println("a/b should be "+"8000");
System.out.println("a/b= "+(A.dividedBy(B).toString()));
System.out.println("-------------------------------------------");
System.out.println("test divide 4th");
A = MyBigInt.valueOf("79845612374984165126541351078411");
B = MyBigInt.valueOf("159753");
System.out.println("a="+A.toString());
System.out.println("b="+B.toString());
System.out.println("a/b should be "+"499806653865555984091324426");
System.out.println("a/b= "+(A.dividedBy(B).toString()));
System.out.println("-------------------------------------------");
System.out.println("test divide 5th");
A = MyBigInt.valueOf("159753");
B = MyBigInt.valueOf("0");
System.out.println("a="+A.toString());
System.out.println("b="+B.toString());
System.out.println("a/b should be "+"invalid");
System.out.println("a/b= "+(A.dividedBy(B).toString()));
System.out.println("-------------------------------------------");
System.out.println("test divide 6th");
A = MyBigInt.valueOf("0");
B = MyBigInt.valueOf("159753");
System.out.println("a="+A.toString());
System.out.println("b="+B.toString());
System.out.println("a/b should be "+"0");
System.out.println("a/b= "+(A.dividedBy(B).toString()));
System.out.println("-------------------------------------------");
}
}
由于我不是计算机专业的,算法与数据结构部分一直是我的软肋。我从别人的csdn blog学到很多好东西,受益匪浅,因此我也分享我的一些学习历程。