Java Karatsuba算法的效率

Java Karatsuba算法的效率,java,performance,biginteger,Java,Performance,Biginteger,我正在尝试构建自己版本的RSA密码。 问题之一是要有一种快速有效的方法将两个数字相乘。我自己对biginger代码的理解不足以计算其方法multiply()的时间复杂度,而评论称其复杂度为O(n²)。我决定找到Karatsuba算法的代码并进行测试。 结果是。。。奇怪 几乎每次普通乘法算法都比Karatsuba算法工作得更好,不管两个数字的位数或变量bitsforkaratsuba效率的限制(即两个数字必须具有的位数,Karatsuba在理论上更有效) 现在,我研究了算法和实际实现:Karats

我正在尝试构建自己版本的RSA密码。
问题之一是要有一种快速有效的方法将两个数字相乘。我自己对
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;
      }
  }