对于java来说,每一个整数运算都是int型,为了能运用cpu本身的乘法且不溢出,所以选用int[]数组来存储结果,在BigInteger的源代码中,还有另外两个算法,有两个限值
private static final int KARATSUBA_THRESHOLD = 80; //数据的Byte数=80*4
private static final int TOOM_COOK_THRESHOLD = 240; //数据的Byte数=240*4
当任一乘数的表示需要的int数小于80时,选用multilplyToLen方法,当两个乘数的int数都小于240且大于80时,选用KARATSUBA,否则选用TOOM_COOK,这两种算法都是基于分治法来实现,然后利用多项式减少乘法的次数。
如果每次只取出一位十进制数做乘法运算,cpu执行的仍然是int型的乘法,很浪费,可以一次取出9位,然后做运算。
重新优化了一下循环中的代码
/*
大整數乘法,@see BigInteger#multiplyToLen
*/
public static String multiply(String x, String y) {
int len1 = x.length();
int len2 = y.length();
int l1 = len1 / 9;
int rem1 = len1 % 9;
if (rem1 !=0) {
l1++;
}
int[] xArr = extract(x, l1, rem1);
int l2 = len2 / 9;
int rem2 = len2 % 9;
if (rem2 !=0) {
l2++;
}
int[] yArr = extract(y, l2, rem2);
int[] res = new int[l1+l2];
int xstart=l1-1;
int ystart=l2-1;
long L_MASK = 1000000000L;
long carry = 0;
int k = l1 + l2 - 1;
for (int j = ystart; j >=0; j--, k --) {
long product = ((long) yArr[j]) * xArr[xstart] + carry;
res[k] = (int) (product % L_MASK);
carry = product / L_MASK;
}
res[k] = (int) carry;
for (int i = xstart -1; i >=0; i--) {
carry = 0;
k = l2 + i;
for (int j = ystart; j >=0; j --, k--) {
long product = ((long) yArr[j]) * xArr[i] + res[k] + carry;
res[k] = (int) (product % L_MASK);
carry = product / L_MASK;
}
res[k] = (int) carry;
}
StringBuilder ans = new StringBuilder();
boolean flag = false;
for (int i = 0; i < res.length; i++) {
if (flag==false) {
if (res[i] != 0) {
flag = true;
ans.append(Integer.toString(res[i]));
}
}else {
String s = Integer.toString(res[i]);
for (int i1 = 0; i1 < 9 - s.length(); i1++) {
ans.append('0');
}
ans.append(s);
}
}
return ans.toString();
}
private static int[] extract(String s, int len, int rem) {
int[] arr = new int[len];
for (int i = 0; i < len; i++) {
int temp = i * 9;
if (rem != 0) {
if (i == 0) {
arr[i] = Integer.valueOf(s.substring(0, rem));
} else {
arr[i] = Integer.valueOf(s.substring(rem + temp - 9, rem + temp));
}
continue;
}
arr[i] = Integer.valueOf(s.substring(temp, temp + 9));
}
return arr;
}
2.性能测试结果
代码:
public static void main(String[] args) {
String s1 = "1325134651345642355434444462523452345632458888888888888888888888888888888888888888888888888888888888888888888888";
String s2 = "13251346513456423554734564555555537388888888888888888888888888888888888888888888888888888888888888888888888888880000001";
String multiply1 = null;
String multiply2 = null;
long t1 = System.nanoTime();
for (int i = 0; i < 100000; i++) {
multiply1 = new BigInteger(s1).multiply(new BigInteger(s2)).toString();
}
long t2 = System.nanoTime();
for (int i = 0; i < 100000; i++) {
multiply2 = multiply(s1, s2);
}
long t3 = System.nanoTime();
System.out.println("" + (t2 - t1) / 100000.0 + "ns " + (t3 - t2) / 100000.0 + "ns");
System.out.println(multiply1);
System.out.println(multiply2);
}