算法概述
Karatsuba算法由Anatolii Karatsuba于1960年提出,是首个突破O(n²)时间复杂度的大整数乘法算法。传统竖式乘法需要O(n²)次单位数字乘法,而Karatsuba算法利用分治策略,将n位大整数拆分为两部分,通过3次递归乘法而非4次,将复杂度降至O(n^log₂3)≈O(n^1.585)。这一成果开创了快速算术算法的先河,后续的Toom-Cook算法和Schönhage-Strassen算法都是在此基础上的进一步发展。
核心思想
将两个n位大整数X和Y分别拆分为高位和低位:
– X = A × 10^(n/2) + B
– Y = C × 10^(n/2) + D
传统方法计算XY = AC × 10^n + (AD + BC) × 10^(n/2) + BD,需要4次乘法。Karatsuba观察到:
AD + BC = (A + B)(C + D) – AC – BD
因此只需计算3个乘积:AC、BD、(A+B)(C+D),再用加减法组合得到结果。
Java实现
import java.math.BigInteger;
/**
* Karatsuba大整数快速乘法算法实现
* 时间复杂度:O(n^1.585)
* 空间复杂度:O(n)
*/
public class KaratsubaMultiplication {
// 当数字位数小于此阈值时,使用普通乘法
private static final int THRESHOLD = 32;
/**
* Karatsuba乘法入口
* @param x 第一个大整数
* @param y 第二个大整数
* @return 乘积
*/
public static BigInteger karatsuba(BigInteger x, BigInteger y) {
// 处理负数情况
boolean negative = false;
if (x.signum() < 0) {
x = x.negate();
negative = !negative;
}
if (y.signum() < 0) {
y = y.negate();
negative = !negative;
}
BigInteger result = karatsubaPositive(x, y);
return negative ? result.negate() : result;
}
/**
* 递归实现Karatsuba乘法(仅处理非负数)
*/
private static BigInteger karatsubaPositive(BigInteger x, BigInteger y) {
// 小数字使用内置乘法(更高效)
int n = Math.max(x.bitLength(), y.bitLength());
if (n <= THRESHOLD) {
return x.multiply(y);
}
// 计算分割位数(按二进制位分割)
int half = (n / 2) + (n % 2);
BigInteger splitPoint = BigInteger.ONE.shiftLeft(half);
// 拆分为高位和低位
// x = a * 2^half + b
// y = c * 2^half + d
BigInteger a = x.shiftRight(half);
BigInteger b = x.and(splitPoint.subtract(BigInteger.ONE));
BigInteger c = y.shiftRight(half);
BigInteger d = y.and(splitPoint.subtract(BigInteger.ONE));
// 递归计算3个乘积
BigInteger ac = karatsubaPositive(a, c); // a * c
BigInteger bd = karatsubaPositive(b, d); // b * d
BigInteger abcd = karatsubaPositive(a.add(b), c.add(d)); // (a+b)*(c+d)
// ad + bc = (a+b)(c+d) - ac - bd
BigInteger ad_bc = abcd.subtract(ac).subtract(bd);
// 组合结果:ac * 2^(2*half) + (ad+bc) * 2^half + bd
return ac.shiftLeft(2 * half)
.add(ad_bc.shiftLeft(half))
.add(bd);
}
/**
* 十进制字符串版本的Karatsuba乘法
* 更直观展示算法原理
*/
public static String karatsubaString(String x, String y) {
// 处理前导零和符号
boolean negative = false;
if (x.startsWith("-")) {
x = x.substring(1);
negative = !negative;
}
if (y.startsWith("-")) {
y = y.substring(1);
negative = !negative;
}
// 移除前导零
x = x.replaceFirst("^0+", "");
y = y.replaceFirst("^0+", "");
if (x.isEmpty()) x = "0";
if (y.isEmpty()) y = "0";
String result = karatsubaStringPositive(x, y);
return negative ? "-" + result : result;
}
private static String karatsubaStringPositive(String x, String y) {
int n = Math.max(x.length(), y.length());
// 小数字使用普通乘法
if (n <= THRESHOLD / 4) {
return multiplySimple(x, y);
}
// 补零使长度为2的幂次方便分割
int half = (n / 2) + (n % 2);
x = padLeft(x, half * 2);
y = padLeft(y, half * 2);
// 分割
String a = x.substring(0, half);
String b = x.substring(half);
String c = y.substring(0, half);
String d = y.substring(half);
// 递归计算
String ac = karatsubaStringPositive(a, c);
String bd = karatsubaStringPositive(b, d);
String abcd = karatsubaStringPositive(addString(a, b), addString(c, d));
// (a+b)(c+d) - ac - bd
String ad_bc = subtractString(subtractString(abcd, ac), bd);
// 结果 = ac * 10^(2*half) + ad_bc * 10^half + bd
String result = addString(
addString(ac + "0".repeat(2 * half), ad_bc + "0".repeat(half)),
bd
);
return result.replaceFirst("^0+", "");
}
/**
* 简单的大整数乘法(竖式乘法)
*/
private static String multiplySimple(String x, String y) {
BigInteger a = new BigInteger(x);
BigInteger b = new BigInteger(y);
return a.multiply(b).toString();
}
/**
* 大整数加法(字符串实现)
*/
private static String addString(String a, String b) {
StringBuilder sb = new StringBuilder();
int carry = 0;
int i = a.length() - 1, j = b.length() - 1;
while (i >= 0 || j >= 0 || carry > 0) {
int sum = carry;
if (i >= 0) sum += a.charAt(i--) - '0';
if (j >= 0) sum += b.charAt(j--) - '0';
sb.append(sum % 10);
carry = sum / 10;
}
return sb.reverse().toString();
}
/**
* 大整数减法(字符串实现,假设a>=b)
*/
private static String subtractString(String a, String b) {
StringBuilder sb = new StringBuilder();
int borrow = 0;
int i = a.length() - 1, j = b.length() - 1;
while (i >= 0) {
int diff = (a.charAt(i) - '0') - borrow - (j >= 0 ? b.charAt(j) - '0' : 0);
if (diff < 0) {
diff += 10;
borrow = 1;
} else {
borrow = 0;
}
sb.append(diff);
i--; j--;
}
String result = sb.reverse().toString().replaceFirst("^0+", "");
return result.isEmpty() ? "0" : result;
}
/**
* 左侧补零
*/
private static String padLeft(String s, int length) {
if (s.length() >= length) return s;
return "0".repeat(length - s.length()) + s;
}
public static void main(String[] args) {
// 测试用例1:普通整数
BigInteger a = new BigInteger("12345678901234567890");
BigInteger b = new BigInteger("98765432109876543210");
BigInteger karatsubaResult = karatsuba(a, b);
BigInteger standardResult = a.multiply(b);
System.out.println("Karatsuba结果: " + karatsubaResult);
System.out.println("标准乘法结果: " + standardResult);
System.out.println("结果一致: " + karatsubaResult.equals(standardResult));
// 测试用例2:超大整数
String x = "314159265358979323846264338327950288419716939937510";
String y = "271828182845904523536028747135266249775724709369995";
String stringResult = karatsubaString(x, y);
String expected = new BigInteger(x).multiply(new BigInteger(y)).toString();
System.out.println("\n超大整数测试一致: " + stringResult.equals(expected));
}
}
复杂度分析
| 算法 | 时间复杂度 | 说明 |
|---|---|---|
| 竖式乘法 | O(n²) | 逐位相乘再相加 |
| Karatsuba | O(n^1.585) | 3次递归调用,每次规模n/2 |
| Toom-Cook | O(n^1.465) | 将数字分成更多部分 |
| Schönhage-Strassen | O(n log n log log n) | 基于FFT的乘法 |
Karatsuba的递归关系式为T(n) = 3T(n/2) + O(n),根据主定理,a=3, b=2, log₂3≈1.585,因此时间复杂度为O(n^1.585)。
关键优化点
- 阈值切换:当数字位数较少时(通常32~64位),直接使用硬件乘法或内置大整数乘法,避免递归开销。
- 二进制分割:使用位运算而非十进制分割,减少进制转换开销。
- 内存复用:在实际工程中,可通过内存池复用临时数组,降低GC压力。
应用场景
Karatsuba算法是密码学、科学计算和高精度数学库的基础组件。RSA加密中的大数模幂运算、π的高精度计算、金融系统的精确账务计算等场景都依赖高效的大整数乘法。Java的BigInteger类在数字足够大时也会自动切换到Karatsuba或更高级的乘法算法。