Java Karatsuba算法的效率
我正在尝试构建自己版本的RSA密码。Java Karatsuba算法的效率,java,performance,biginteger,Java,Performance,Biginteger,我正在尝试构建自己版本的RSA密码。 问题之一是要有一种快速有效的方法将两个数字相乘。我自己对biginger代码的理解不足以计算其方法multiply()的时间复杂度,而评论称其复杂度为O(n²)。我决定找到Karatsuba算法的代码并进行测试。 结果是。。。奇怪 几乎每次普通乘法算法都比Karatsuba算法工作得更好,不管两个数字的位数或变量bitsforkaratsuba效率的限制(即两个数字必须具有的位数,Karatsuba在理论上更有效) 现在,我研究了算法和实际实现:Karats
问题之一是要有一种快速有效的方法将两个数字相乘。我自己对
biginger
代码的理解不足以计算其方法multiply()
的时间复杂度,而评论称其复杂度为O(n²)。我决定找到Karatsuba算法的代码并进行测试。结果是。。。奇怪 几乎每次普通乘法算法都比Karatsuba算法工作得更好,不管两个数字的位数或变量
bitsforkaratsuba效率的限制(即两个数字必须具有的位数,Karatsuba在理论上更有效)
现在,我研究了算法和实际实现:Karatsuba应该在理论和实践上都获胜。有人知道为什么测试支持通用乘法算法吗
我使用的代码如下。我只调整了两行代码:limitOfBitsForKaratsubaEfficiency
和intn=Integer.parseInt(“100000”)代码>
据
两个n位数字相乘的标准过程需要一系列与n^2\,!,或θ(n^2)\,!,成比例的初等运算
因此,您可以期待相同的大O,但是如果不访问原始基础数据,实现将产生更多的垃圾,并且效率可能会降低。您的实现似乎是O(N^2)但效率低于内置实现。最简单的答案可能是biginger
的实现者知道他们在做什么。@PeterLawrey:Karatsuba是O(n 1.585),而bitineger.multiply
应该是O(n²)。我用bitineger.multiply
使用的代码编辑了我的原始帖子@路易斯瓦瑟曼:他们可能知道,但我还是不明白……两个n位数字相乘的标准程序不是Karatsuba算法:后者的复杂度为O(nlog 3)。这就是为什么我觉得很奇怪,代码比biginger
乘法方法花费的时间更多。@Argonath Wikipedia建议它是O(n^2),就像你的代码和结果一样。你从哪里得到O(n log3)?@PeterLawrey维基百科并不这么认为。O(n^1.585)是从这篇文章中,我甚至不知道你从哪里得到的O(n^2)-如果它是从你引用的部分,那么它与那篇文章应该得到的正好相反。标准程序是O(n^2),这就是为什么Karatsuba更好,而不是为什么它是一样的,因为它不是。
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;
/** 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;
}
}