算法与数据结构学习之——大数字四则运算


这里分享我自己写的一个大数字的四则运算的实现,乘法是我首先实现的,是参考别人的,加法、减法、除法和比较大小是我自己摸索着写的。

加法、减法和乘法是可以独立写成的,而除法就比较麻烦,需要比较大小、减法和乘法的辅助才能完成。

四则运算的实现方法是我们小学时用的算式法,比较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学到很多好东西,受益匪浅,因此我也分享我的一些学习历程。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值