Karatsuba 算法的效率
Posted
技术标签:
【中文标题】Karatsuba 算法的效率【英文标题】:Efficiency of Karatsuba algorithm 【发布时间】:2015-08-24 18:28:28 【问题描述】:我正在尝试构建自己的 RSA 密码版本。
问题之一是有一种快速有效的方法来将两个数字相乘。我不明白BigInteger
代码足以自己计算其方法multiply()
的时间复杂度,而评论说复杂度是 O(n²) 。我决定找到 Karatsuba 算法的代码并进行测试。
结果……很奇怪。
几乎每次,普通乘法算法的效果都比 Karatsuba 好,无论这两个数字的位数或变量 limitOfBitsForKaratsubaEfficiency
(即两个数字必须具有的位数) Karatsuba 更有效...理论上)。
现在,我研究了算法和实际实现:Karatsuba 应该在理论上和实践上都获胜。有人知道为什么测试偏爱通用乘法算法吗?
我使用的代码如下。我只调整了两行代码:limitOfBitsForKaratsubaEfficiency
和 int N = Integer.parseInt("100000");
public static BigInteger karatsuba(BigInteger x, BigInteger y)
// cutoff to brute force
int N = Math.max(x.bitLength(), y.bitLength());
if (N <= limitOfBitsForKaratsubaEfficiency) return x.multiply(y); // optimize this parameter
// number of bits divided by 2, rounded up
N = (N / 2) + (N % 2);
// x = a + 2^N b, y = c + 2^N d
BigInteger x1 = x.shiftRight(N);
BigInteger x0 = x.subtract(x1.shiftLeft(N));
BigInteger y1 = y.shiftRight(N);
BigInteger y0 = y.subtract(y1.shiftLeft(N));
// compute sub-expressions
BigInteger ac = karatsuba(x0, y0);
BigInteger bd = karatsuba(x1, y1);
BigInteger abcd = karatsuba(x0.add(x1), y0.add(y1));
return ac.add(abcd.subtract(ac).subtract(bd).shiftLeft(N)).add(bd.shiftLeft(2*N));
public static void main(String[] args)
long start, stopKara, stopNorma;
Random random = new Random();
int N = Integer.parseInt("100000");
BigInteger a,b,c,d;
for(int i=0 ; i<15 ; i++)
a = new BigInteger(N, random);
b = new BigInteger(N, random);
System.out.printf("The numbers to be multiplied are: \n\t %s \n\t %s\n", a.toString(), b.toString());
start = System.nanoTime();
c = karatsuba(a, b);
stopKara = System.nanoTime();
stopKara = stopKara - start;
System.out.printf("The karatsuba algorithm has computed %d milliseconds.\n", stopKara);
start = System.nanoTime();
d = a.multiply(b);
stopNorma = System.nanoTime();
stopNorma = stopNorma - start;
System.out.printf("The common multiplication algorithm has computed %d milliseconds.\n", stopNorma);
if(c.equals(d)) System.out.println("The two products are equals.");
else System.out.println("The two products are NOT equals: the karatsuba method does not works!");
System.out.printf("The difference Time(Karatsuba)-Time(normalAlgorithm) is: \t %d", stopKara - stopNorma);
System.out.printf("\n\n\n");
static BigInteger TWO = BigInteger.valueOf(2);
static BigInteger ONE = BigInteger.ONE;
static int limitOfBitsForKaratsubaEfficiency = 640;
编辑:我找到了BigInteger.multiply()
使用的两种方法。我绝对不是位对位操作方面的专家,但是这两个for
-cicles 让我觉得复杂度是 O(n²)。 Karatsuba 应该是 O(n
1.585
)。
/** Multiply x[0:len-1] by y, and write the len least
* significant words of the product to dest[0:len-1].
* Return the most significant word of the product.
* All values are treated as if they were unsigned
* (i.e. masked with 0xffffffffL).
* OK if dest==x (not sure if this is guaranteed for mpn_mul_1).
* This function is basically the same as gmp's mpn_mul_1.
*/
public static int mul_1 (int[] dest, int[] x, int len, int y)
long yword = (long) y & 0xffffffffL;
long carry = 0;
for (int j = 0; j < len; j++)
carry += ((long) x[j] & 0xffffffffL) * yword;
dest[j] = (int) carry;
carry >>>= 32;
return (int) carry;
/**
* Multiply x[0:xlen-1] and y[0:ylen-1], and
* write the result to dest[0:xlen+ylen-1].
* The destination has to have space for xlen+ylen words,
* even if the result might be one limb smaller.
* This function requires that xlen >= ylen.
* The destination must be distinct from either input operands.
* All operands are unsigned.
* This function is basically the same gmp's mpn_mul. */
public static void mul (int[] dest,
int[] x, int xlen,
int[] y, int ylen)
dest[xlen] = MPN.mul_1 (dest, x, xlen, y[0]);
for (int i = 1; i < ylen; i++)
long yword = (long) y[i] & 0xffffffffL;
long carry = 0;
for (int j = 0; j < xlen; j++)
carry += ((long) x[j] & 0xffffffffL) * yword
+ ((long) dest[i+j] & 0xffffffffL);
dest[i+j] = (int) carry;
carry >>>= 32;
dest[i+xlen] = (int) carry;
【问题讨论】:
您的实现似乎是 O(N^2),但效率低于内置实现。 最简单的答案可能是BigInteger
的实现者知道他们在做什么。
@PeterLawrey : Karatsuba 是 O(n 1.585 ),而 BitInteger.multiply
应该 是 O(n²)。我使用BitInteger.multiply
使用的代码编辑了我的原始帖子。 @LouisWasserman:他们可能知道,但我还是不明白...
【参考方案1】:
根据https://en.wikipedia.org/wiki/Karatsuba_algorithm
“两个 n 位数字相乘的标准过程需要许多与 n^2\,!, 或 \Theta(n^2)\,! 成比例的基本运算。”
因此,您可以期待相同的 big-O,但实现会产生更多垃圾,并且如果不访问原始底层数据,效率可能会降低。
【讨论】:
两个 n 位数字相乘的标准过程不是 Karatsuba 算法:后者的复杂度为 O(n日志 3)。这就是为什么我发现代码比BigInteger
乘法方法花费更多时间的原因。
@Argonath Wikipedia 建议它是 O(n^2) ,您的代码和结果也是如此。你从哪里得到 O(n log 3)?
@PeterLawrey ***不建议这样做。 O(n^1.585) 从字面上看来自文章,我什至不知道你从哪里得到 O(n^2) - 如果它来自你引用的部分,那么它与从中得到的内容完全相反文章应该是。标准程序是 O(n^2),这就是 Karatsuba 更好的原因,而不是相同的原因,因为它不是。以上是关于Karatsuba 算法的效率的主要内容,如果未能解决你的问题,请参考以下文章