大数乘法
基本思想:
输入字符串,转成char数组,转成int数组。采用分治思想,每一位的相乘;
- 公式:AB*CD = AC (BC+AD) BD
=任意位数的整数相乘,最终都是可以转化为两位数相乘。但是,不同位的两位数乘的结果,最后应该如何拼接呢?这需要我们来找下更深层次的规律。分析一下四位数的乘法
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(低位)
高位高位结果:1256=672
高位低位+低位高位:1278+3456=936+1094=2840
低位低位结果:3478=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,凑成一样位数的即可,不赘述。)
代码如下:
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);
} 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())));
}
}