数组实现大数加法及乘法

大致思路

在计算大数字的时候可以使用数组来储存数字,然后通过数组操作来完成数学运算。

大数加法:数组对位运算,将大数储存到数组中之后每一位做简单的加法运算,思路很简单

大数乘法:我们可以将超大整数转化若干的小操作。
例如:
a b c d e × p q r s t = a b c d e × p × 10000 + a b c d e × q × 1000 + a b c d e × r × 100 + a b c d e × s × 10 + a b c d e × t × 1 \begin{aligned} abcde \times pqrst & = abcde \times p \times 10000 \\ & +abcde \times q \times 1000 \\ & +abcde \times r \times 100 \\ & +abcde \times s \times 10 \\ & +abcde \times t \times 1 \end{aligned} abcde×pqrst=abcde×p×10000+abcde×q×1000+abcde×r×100+abcde×s×10+abcde×t×1上面这样子就可以把大数乘法划分成较小的数值运算。
我们将其划分成一个大数与一个个位数相乘,再乘以一个10的幂次;
大数与仅有个位的数相乘的思路,在代码实现上和相加大同小异;
至于乘以10的运算,则可以通过数组位移实现,稍后会讲;
最后,而每一个子项都可以进行一次加法运算,如:
a b c d e × p × 10000 + a b c d e × q × 1000 abcde \times p \times 10000+abcde \times q \times 1000 abcde×p×10000+abcde×q×1000可以视为两个大数的加法

借助数组存储大数数值

考虑到位数和权重是等价的,第一位(个位)的权重为0(即100),第二位(十位)的权重为1(即101),所以在大数储存中为了保留权重信息,我们应该将数字倒序存储
比如:存储数字 12345 12345 12345
因为:
12345 = 1 × 1 0 4 + 2 × 1 0 3 + 3 × 1 0 2 + 4 × 1 0 1 + 5 × 1 0 0 12345 = 1 \times 10^4 + 2 \times 10^3 + 3 \times 10^2 + 4 \times 10^1 + 5 \times 10^0 12345=1×104+2×103+3×102+4×101+5×100
所以数组应该储存为:arr=[5, 4, 3, 2, 1];
此时,arr[0] = 5arr[4] = 1,下标对应指数。

在一个数乘100时,只需要将数组的数值向后移两位,前两位补零即可(因为是倒序存储所以不能从后面补零)。还是上面这个例子,如果要计算 12345 × 100 12345 \times 100 12345×100,只需要将数组前补两个0:arr=[0, 0, 5, 4, 3, 2, 1];
这样子表示的数字就是 1234500 1234500 1234500

思路到这里就差不多了,下面是实现代码,已经写了一些注释。可能在结果输出那里有点难以理解,因为会有多余的0需要检查,可以做一下debug试试。

大数类LargeNumber :

import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class LargeNumber {
	private int[] nums;

	public int[] getNums() {
		return nums;
	}

	//私有化,不允许外部改变nums数组
	private void setNums(int[] nums) {
		this.nums = nums;
	}

	@Override
	public String toString() {
		int len = nums.length;
		//将大数拼接成字符串输出
		StringBuilder s = new StringBuilder();
		//数字之前如果为0,且不只有一位数字,不输出最高位的0
		//如0159,只输出159
		boolean b = true;
		/* 在乘法运算时发现,有时候结果数字会有很多个0,
		如 10*9,运算中在exponentArithmetic方法会算出090,
		在addByArray方法中会算出0090,如果只判断第一个数字是否为0,
		则最终输出结果为 10*9=090,不够理想,
		
		所以此处判断前n项是否会0序列串,
		如果前n想均为0,则不输出,只输出数字串,如000010230 输出 10230
		*/
		for (int i = len - 1; i >= 0; i--) {
			if (nums[i] == 0 && b) {
				continue;
			} else if (nums[i] != 0) {
				b = false;
			}
			s.append(nums[i]);
		}
		String result = s.toString();
		if (result.equals("")) {
			return "0";
		}
		return result;
	}

	private LargeNumber() {
	}

	private LargeNumber(String num) {
		try {
			nums = setNumArray(num);
		} catch (Exception e) {
			e.printStackTrace();
			System.exit(0);
		}
	}

	/** 通过数字串创建 */
	public static LargeNumber getLargeNumber(String n) {
		return new LargeNumber(n);
	}

	/** 通过数组创建 */
	public static LargeNumber getLargeNumber(int[] a) {
		LargeNumber largeNumber = new LargeNumber();
		largeNumber.setNums(a);
		return largeNumber;
	}

	/** 将字符串转为int数组 */
	private int[] setNumArray(String n) throws NumberFormatException {
		//异常判断,只允许输入纯数字串
		String pattern = "^[1-9][0-9]*$";
		Pattern r = Pattern.compile(pattern);
		Matcher m = r.matcher(n);
		if (!m.find()) {
			if (!n.equals("0")) {
				throw new NumberFormatException("请输入纯数字串");
			}
		}
		int[] nums;
		int len = n.length();
		nums = new int[len];
		//大数数组倒序赋值,下标即位数权重。
		//比如 321 = 1*10^0 + 2*10^1 + 3*10^2
		for (int i = 0; i < len; i++) {
			char c = n.charAt(i);
			//将字符转为数字存入数组
			nums[len - i - 1] = Integer.parseInt(String.valueOf(c));
		}
		return nums;
	}

	/** 大数加法 */
	public LargeNumber add(LargeNumber ln) {
		//获取两个大数数组来相加
		int[] nums1 = this.getNums();
		int[] nums2 = ln.getNums();

		//通过方法将两个大数数组相加
		//再通过数组创建大数类型,并返回
		return LargeNumber.getLargeNumber(addByArray(nums1, nums2));
	}

	/** 将两个大数数组相加 */
	private int[] addByArray(int[] nums1, int[] nums2) {
		/*
		 获取较长的数组,并将两个数组的长度设为相同
		 避免后续for循环超出数组长度
		 */
		int maxLen;
		if (nums1.length > nums2.length) {
			maxLen = nums1.length;
			nums2 = Arrays.copyOf(nums2, nums1.length);
		} else {
			maxLen = nums2.length;
			nums1 = Arrays.copyOf(nums1, nums2.length);
		}
		//以较长的数组长度+1来初始化结果数组
		//因为加法的最大结果会大一位数
		int[] result = new int[maxLen + 1];
		//进位单元
		int temp = 0;
		for (int i = 0; i < maxLen; i++) {
			int add = nums1[i] + nums2[i];//对于单位相加
			int tens = add / 10;//获取十位数 (进位单元)
			int single = add % 10;//获取个位数
			//将个位数和上个循环的十位数添加到结果数组
			int res = single + temp;
			//如果它们的和大于10,只添加个位
			//十位数再次添加到进位单元
			int tens2 = res / 10;
			int single2 = res % 10;
			result[i] = single2;
			//十位数在下个循环添加
			//如果该次循环没有进位单元,则赋值结果为0
			temp = tens + tens2;
		}
		//将最后一个进位单元添加,如果最后没有进位单元,则为0,不影响结果
		result[maxLen] = temp;
		return result;
	}

	/**
	 * 大数乘法
	 * @param ln
	 * @return
	 */
	public LargeNumber multiply(LargeNumber ln) {
		//获取两个大数数组来相加
		int[] nums1 = this.getNums();
		int[] nums2 = ln.getNums();
		// 获取较长的数组,并将两个数组的长度设为相同
		// 避免后续for循环超出数组长度
		int maxLen;
		if (nums1.length > nums2.length) {
			maxLen = nums1.length;
		} else {
			maxLen = nums2.length;
			//保证nums1数字更大,方便后续乘法运算计算位数
			int[] nums3 = nums1;
			nums1 = nums2;
			nums2 = nums3;
		}
		int[] prev = null;
		for (int i = 0; i < maxLen; i++) {
			//取较大数nums1中的一位数,与nums2相乘,获取结果数组
			int[] temp = multipyWithSingle(nums2, nums1[i]);
			//算上权重
			temp = exponentArithmetic(temp, i);
			if (prev != null) {
				//把这一次的结果和上一次的结果加起来
				prev = addByArray(temp, prev);
			} else {
				prev = temp;
			}
		}
		//通过数组创建大数类型,并返回
		return LargeNumber.getLargeNumber(prev);
	}

	/**
	 * @param nums      被乘大数数组
	 * @param singleNum 将大数数组扩大singleNum倍
	 */
	private int[] multipyWithSingle(int[] nums, int singleNum) {
		assert singleNum / 10 == 0;
		int[] result = new int[nums.length + 1];

		//进位单元
		int temp = 0;
		for (int i = 0; i < nums.length; i++) {

			int multiply = nums[i] * singleNum;
			//获取十位数 (进位单元)
			int tens = multiply / 10;
			//获取个位数
			int single = multiply % 10;
			//将个位数和上个循环的十位数添加到结果数组
			int res = single + temp;
			//如果它们的和大于10,只添加个位
			//十位数再次添加到进位单元
			int tens2 = res / 10;
			int single2 = res % 10;
			result[i] = single2;
			//十位数在下个循环添加
			//如果该次循环没有进位单元,则将被设为0
			temp = tens + tens2;
		}
		result[nums.length] = temp;
		return result;
	}

	/** 指数运算 */
	private int[] exponentArithmetic(int[] nums, int exponent) {
		int[] result = new int[nums.length + exponent];
		System.arraycopy(nums, 0, result, exponent, nums.length);
		return result;
	}

	private String outputLargeNumberArray(int[] nums) {
		return LargeNumber.getLargeNumber(nums).toString();
	}
}

测试类LargeNumberCalculate :

import java.util.Scanner;

public class LargeNumberCalculate {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		System.out.println("测试:1234566*89 = " + LargeNumber.getLargeNumber("1234567").multiply(LargeNumber.getLargeNumber("89")));
		while (sc.hasNext()) {
			String n1 = sc.next();
			String n2;
			if (n1.contains("+")) {
				/* 有“+”号,直接计算运算公式,方便调试,
				通过+分割。正则表达式中 + 需要使用\\转义分开 */
				n2 = n1.split("\\+")[1];
				n1 = n1.split("\\+")[0];
				LargeNumber ln1 = LargeNumber.getLargeNumber(n1);
				LargeNumber ln2 = LargeNumber.getLargeNumber(n2);
				LargeNumber add = ln1.add(ln2);
				System.out.println(ln1 + " + " + ln2 + " = " + add);
			} else if (n1.contains("*")) {
				n2 = n1.split("\\*")[1];
				n1 = n1.split("\\*")[0];
				LargeNumber ln1 = LargeNumber.getLargeNumber(n1);
				LargeNumber ln2 = LargeNumber.getLargeNumber(n2);
				LargeNumber add = ln1.multiply(ln2);
				System.out.println(ln1 + " * " + ln2 + " = " + add);
			}
		}
	}
}

控制台结果

控制台结果样例

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值