大数相乘问题--算法思想及Java实现解析(附详细注释)

大整数乘法():

两个乘数比较大,最后结果超过了整型甚至长整型的最大范围,如果要得到精确结果,常规计算方法已经不适用。

这里采用分治的思想,将乘数“分割”,将大整数计算转换为小整数计算。 (附:博文针对正整数计算

思路解析:

   3  4  (大的整数作为被乘数恐怕是很多人的习惯,便于计算,哈哈) 
 *1  2 
——— 
   6  8 
3 4 
——— 
4 0  8 

找规律。其实大家多做几个两位数的乘法,都会发现这个规律:

AB * CD = AC (AD+BC) BD (数位组合)。没错,这里如果BD相乘超过一位数,需要进位(例题中,二四得八,没有进位);(AD+BC+低位进位)如果超过一位数,需要进位(就像刚才的3*1,最后+1得4的操作)。 


此时可以看出,任意位数的整数相乘,最终都是可以转化为两位数相乘。但是,不同位的两位数乘的结果,最后应该如何拼接呢?这需要我们来找下更深层次的规律。分析一下四位数的乘法,找找感觉: 
             1  2  3  4 
          *  5  6  7  8 
———————— 
              9  8  7  2 
          8  6  3  8 
      7  4  0  4 
 6  1  7  0 
———————— 
 7  0  0  6  6  5  2 
结果看起来没什么特别的,如果按照我们分治的思想,转换为两位数相乘,之间能否有些关系呢? 
1234分为 12(高位)和34(低位);5678分为56(高位)和78(低位) 
高位*高位结果:12*56=672 
高位*低位+低位*高位:12*78+34*56=936+1094=2840 
低位*低位结果:34*78=2652 

最后,拼接。需要说明的是,刚才我们提到两位数分解成一位数相乘的规则:超过一位数,需要进位。同理(这里就不证明了),两位数乘以两位数,结果超过两位数,也要进位。 
从低位开始:低两位:2652,26进位,低位保留52;中间两位,2840+26(低位进位)=2866,28进位,中位保留66;高位,672+28(进位)=700,7进位,高位保留00。再往上就没有了,现在可以拼接起来:最高位进位7,高两位00,中位66,低位52,最后结果:7006652。 

规律找到!任意位数(例如N位整数相乘),都可以用这种思想实现:低位保留 N 位数字串,多余高位进位;高位要加上低位进位,如果超过 N 位,依然只保留 N 位,高位进位。(如果是M位整数乘以N位整数怎么办?高位补0,凑成一样位数的即可,不赘述。) 
分治的规律找到了,接下来就是具体实现的思想了。 
没啥新花样,依然是递归思想(这里为了简化,就不递归到两位数相乘了,4位数相乘,计算机还是能够得到精确值的): 
1、如果两个整数M和N的长度都小于等于4位数,则直接返回M*N的结果的字符串形式。 
2、如果如果M、N长度不一致,补齐M高位0(不妨设N>M),使都为N位整数。 
3、N/2 取整,得到整数的分割位数。将M、N拆分成m1、m2,n1,n2。 
4、将 m1、n1;m2、n1;m1、n2;m2、n2 递归调用第1步,分别得到结果AC(高位)、BC(中位)、AD(中位)、BD(低位)。 
5、判断BD位是否有进位bd,并截取bd得到保留位BD‘;判断BC+AD+bd是否有进位abcd,并截取进位得到保留位ABCD’;判断AC+abcd是否有进位ac,并截取进位得到保留位AC’。 

6、返回最终大整数相乘的结果:ac AC’ ABCD’ BD’。 

Java 实现源代码:

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

public class  multipliers {

	// 规模只要在这个范围内可以直接计算(整型数值满足)
	private final static int SIZE = 4;

	// 其中,len为X、Y的长度最大值
	private static String bigIntMultiply(String X, String Y, int len) {
		String str = "";

		if (len <= SIZE) { // 少于4位数,可直接计算
			return "" + (Integer.parseInt(X) * Integer.parseInt(Y));
		}

		if (X.length() != Y.length()) { // 长度不同,调用formatNumber方法,补齐X、Y,使之长度相同
			X = formatNumber(X, len);
			Y = formatNumber(Y, len);
		}

		// 将X、Y分别对半分成两部分
		int len1 = len / 2;
		int len2 = len - len1;
		String A = X.substring(0, len1);
		String B = X.substring(len1);
		String C = Y.substring(0, len1);
		String D = Y.substring(len1);

		// 乘法法则,分块处理
		int lenM = Math.max(len1, len2);
		String AC = bigIntMultiply(A, C, len1);
		String AD = bigIntMultiply(A, D, lenM);
		String BC = bigIntMultiply(B, C, lenM);
		String BD = bigIntMultiply(B, D, len2);

		// 注意处理进位的方法,巧妙地运用了字符串的拼接方面
		// 【1】 处理BD,得到原位及进位
		String[] sBD = dealString(BD, len2);
		// 【2】 处理AD + BC的和
		String ADBC = add(AD, BC);
		// 【3】 加上BD的进位
		if (!"0".equals(sBD[1])) {
			ADBC = add(ADBC, sBD[1]);
		}
		// 【4】 得到ADBC的进位
		String[] sADBC = dealString(ADBC, lenM);

		// 【5】 AC加上ADBC的进位
		AC = add(AC, sADBC[1]);
		// 【6】 最终结果
		str = AC + sADBC[0] + sBD[0];

		return str;
	}

	// 两个数字串按位加
	private static String add(String ad, String bc) {
		// 返回的结果
		String str = "";

		// 两字符串长度要相同
		int lenM = Math.max(ad.length(), bc.length());
		ad = formatNumber(ad, lenM);
		bc = formatNumber(bc, lenM);

		// 按位加,进位存储在flag中
		int flag = 0;
		// 按序从后往前按位求和
		for (int i = lenM - 1; i >= 0; i--) {
			int t = flag + Integer.parseInt(ad.substring(i, i + 1))
					+ Integer.parseInt(bc.substring(i, i + 1));
			// 结果超过9,则进位当前位,保留个位数
			if (t > 9) {
				flag = 1;
				t = t - 10;
			} else {
				flag = 0;
			}
			// 拼接结果字符串
			str = "" + t + str;
		}
		if (flag != 0) {
			str = "" + flag + str;
		}
		return str;
	}

	// 处理数字串,分离出进位,String数组第一个为原位数字,第二个为进位
	private static String[] dealString(String ac, int lenn) {
		String[] str = { ac, "0" };

		if (lenn < ac.length()) {
			int t = ac.length() - lenn;
			str[0] = ac.substring(t);
			str[1] = ac.substring(0, t);
			// System.out.println("+++++++++");
			// System.out.println(str[0]);
			// System.out.println(str[1]);
			// System.out.println(t);
		} else {
			// 保证结果length与lenn一致,少于则高位补0
			String result = str[0];
			for (int i = result.length(); i < lenn; i++) {
				result = "0" + result;
			}
			str[0] = result;
		}
		return str;
	}

	// 格式化操作的数字字符串,高位补零
	private static String formatNumber(String x, int len) {
		while (len > x.length()) {
			x = "0" + x;
		}
		return x;
	}

	public static void main(String[] args) {
		String pat = "^[1-9]\\d*$"; // 正则表达式:不以0开头的数字串
		Pattern p = Pattern.compile(pat); // 将给定的正则表达式编译并赋予给Pattern类

		System.out.println("乘数A(不以0开头的正整数):");
		Scanner sc = new Scanner(System.in);
		String A = sc.next();
		Matcher m = p.matcher(A);

		if (!m.matches()) {
			System.out.println("数字不合法!");
			return;
		}

		System.out.println("乘数B(不以0开头的正整数):");
		String B = sc.next();
		m = p.matcher(B);
		if (!m.matches()) {
			System.out.println("数字不合法!");
			return;
		}
		// Math.max(A.length(), B.length())比较读入的字符串的长短
		System.out.println(A + " * " + B + " = "
				+ bigIntMultiply(A, B, Math.max(A.length(), B.length())));
	}
}

运行结果显示:

《大数相乘问题--算法思想及Java实现解析(附详细注释)》

如果还有疑问,请参考源博文:http://www.tuicool.com/articles/zy6vim

    原文作者:大整数乘法问题
    原文地址: https://blog.csdn.net/qq_24452475/article/details/51140195
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞