目录
java大数运算详解【其三】大数乘法之平方算法之按位二次展开式算法
java大数运算详解【其四】大数乘法之平方算法之Karatsuba平方算法
java大数运算详解【其五】大数乘法之平方算法之ToomCook3平方算法
java大数运算详解【其七】大数乘法之Karatsuba乘法和ToomCook3乘法
java大数运算详解【其九】大数除法之试商法(Knuth除法)核心算法
java大数运算详解【其十】大数除法之Burnikel-Ziegler除法算法
2、单位乘法
private static BigInteger multiplyByInt(int[] x, int y, int sign) {
if (Integer.bitCount(y) == 1) {//移位优化
return new BigInteger(shiftLeft(x,Integer.numberOfTrailingZeros(y)), sign);
}
int xlen = x.length;
int[] rmag = new int[xlen + 1];
long carry = 0;
long yl = y & LONG_MASK;
int rstart = rmag.length - 1;
for (int i = xlen - 1; i >= 0; i--) {//循环带进位乘法
long product = (x[i] & LONG_MASK) * yl + carry;
rmag[rstart--] = (int)product;
carry = product >>> 32;
}
if (carry == 0L) {
rmag = java.util.Arrays.copyOfRange(rmag, 1, rmag.length);
} else {
rmag[rstart] = (int)carry;
}
return new BigInteger(rmag, sign);
}
单位乘法,使用循环带进位乘法算法,由于乘法的进位数值所占位数同乘数,所以单位乘法的进位可以用整型存储。
3、经典乘法
/**
* Multiplies int arrays x and y to the specified lengths and places
* the result into z. There will be no leading zeros in the resultant array.
*/
private static int[] multiplyToLen(int[] x, int xlen, int[] y, int ylen, int[] z) {
multiplyToLenCheck(x, xlen);
multiplyToLenCheck(y, ylen);
return implMultiplyToLen(x, xlen, y, ylen, z);
}
@HotSpotIntrinsicCandidate
private static int[] implMultiplyToLen(int[] x, int xlen, int[] y, int ylen, int[] z) {
int xstart = xlen - 1;
int ystart = ylen - 1;
if (z == null || z.length < (xlen+ ylen))
z = new int[xlen+ylen];
long carry = 0;
for (int j=ystart, k=ystart+1+xstart; j >= 0; j--, k--) {//x的最低位整型与y相乘的结果存入z,进位不存入。
long product = (y[j] & LONG_MASK) *
(x[xstart] & LONG_MASK) + carry;
z[k] = (int)product;
carry = product >>> 32;
}
z[xstart] = (int)carry;
for (int i = xstart-1; i >= 0; i--) {
carry = 0;
for (int j=ystart, k=ystart+1+i; j >= 0; j--, k--) {//x的其他位整型与y相乘的结果依次存入z,进位不存入。
long product = (y[j] & LONG_MASK) *
(x[i] & LONG_MASK) +
(z[k] & LONG_MASK) + carry;
z[k] = (int)product;
carry = product >>> 32;
}
z[i] = (int)carry;//设置进位
}
return z;
}
private static void multiplyToLenCheck(int[] array, int length) {
if (length <= 0) {
return; // 不是错误,因为如果len <= 0, multiplyToLen不会执行
}
Objects.requireNonNull(array);
if (length > array.length) {
throw new ArrayIndexOutOfBoundsException(length - 1);
}
}
经典乘法,采用二重循环带进位乘法算法,由于乘法的进位数值所占位数同乘数,所以不应使用循环带进位乘法算法。
若使用循环带进位乘法算法,那么进位值要用一个同乘数长度的数组来存储?(此非明也),随乘数数组长度变长其效率将变得低下。
使用二重循环带进位乘法算法,将其分成多个循环带进位乘法的和,可以较好的完成乘法运算。