Java 使用BigInteger的Karatsuba乘法

Java 使用BigInteger的Karatsuba乘法,java,algorithm,biginteger,Java,Algorithm,Biginteger,我首先用long为Karatsuba算法编写了一个代码。我认为它工作得很好。使用相同的逻辑,我将代码转换为BigInteger,但由于某些原因,它给出了StackOverflowerError 我不明白为什么。请帮忙 EDIT1:长期以来,该代码也存在逻辑缺陷。我不知道是什么 编辑2:长代码现在起作用了。我错误地将“%”运算符切换为“/” 伊迪丝3:现在一切都好了。我将.xor改为.pow,将==改为.equals,并修复了return语句中的一些括号问题。谢谢大家的帮助 以下是正确的代码:

我首先用long为Karatsuba算法编写了一个代码。我认为它工作得很好。使用相同的逻辑,我将代码转换为BigInteger,但由于某些原因,它给出了StackOverflowerError

我不明白为什么。请帮忙


EDIT1:长期以来,该代码也存在逻辑缺陷。我不知道是什么

编辑2:长代码现在起作用了。我错误地将“%”运算符切换为“/”

伊迪丝3:现在一切都好了。我将.xor改为.pow,将==改为.equals,并修复了return语句中的一些括号问题。谢谢大家的帮助


以下是正确的代码:

public static BigInteger karatsuba3(BigInteger i, BigInteger j){
    if (i.compareTo(Ten) == -1 || j.compareTo(Ten) == -1)
        return i.multiply(j);
    String length = getLength(i.max(j));
    BigInteger n = new BigInteger(length);
    if (n.mod(Two).equals(One))
        n = n.add(One);

    BigInteger a = i.divide(Ten.pow(n.divide(Two).intValue()));
    BigInteger b = i.mod(Ten.pow(n.divide(Two).intValue()));
    BigInteger c = j.divide(Ten.pow(n.divide(Two).intValue()));
    BigInteger d = j.mod(Ten.pow(n.divide(Two).intValue()));

    BigInteger first = karatsuba3(a,c);
    BigInteger second = karatsuba3(b,d);
    BigInteger third = karatsuba3(a.add(b),c.add(d));

    return ((first.multiply(Ten.pow(n.intValue()))).add ((((third.subtract(first)).subtract( second))).multiply(Ten.pow(n.divide((new BigInteger("2"))).intValue()))).add(second));
}
长代码的Karatsuba:

public static long karatsuba1(long i, long j){
    if (i < 10 || j < 10) 
        return i*j;
    double n = getLength(Math.max(i,j));
    if (n%2 == 1)
        n++;
    long a = (long) (i/Math.pow(10,(n/2)));
    long b = (long) (i%Math.pow(10,(n/2)));
    long c = (long) (j/Math.pow(10,(n/2)));
    long d = (long) (j%Math.pow(10,(n/2)));

    long first = karatsuba1(a, c);
    long second = karatsuba1(b, d);
    long third = karatsuba1(a + b, c + d);

    return ((long) ((first * Math.pow(10, n)) + ((third - first - second) * Math.pow(10, (n/2))) + second));
}

public static int getLength( long a){
    String b = Long.toString(a);
    return b.length();
}

在第一段代码中,我觉得这一行是错误的:

long d = (long) (j/Math.pow(10,(n/2)));
现在你有了
c=d
您可能想要:

long d = (long) (j%Math.pow(10,(n/2)));

在比较和分配bigint时,您犯了一些错误,因此这就是无限递归调用的原因

if (n.mod(new BigInteger("2")) == new BigInteger("1"))
        n.add(new BigInteger ("1"));
按规定予以纠正,

if (n.mod(new BigInteger("2")).equals(new BigInteger("1")))
      n = n.add(new BigInteger ("1"));
但还有其他问题需要解决,以产生与“长”版本相同的结果。希望您能够纠正它们。

使用Ten.pow(x.intValue())而不是Ten.xor(x)

现在可以了

public static BigInteger karatsuba3(BigInteger x, BigInteger y) {

    // cutoff to brute force
    int N = Math.max(x.bitLength(), y.bitLength());
    if (N <= 2000) 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 b = x.shiftRight(N);
    BigInteger a = x.subtract(b.shiftLeft(N));
    BigInteger d = y.shiftRight(N);
    BigInteger c = y.subtract(d.shiftLeft(N));

    // compute sub-expressions
    BigInteger ac    = karatsuba3(a, c);
    BigInteger bd    = karatsuba3(b, d);
    BigInteger abcd  = karatsuba3(a.add(b), c.add(d));

    return ac.add(abcd.subtract(ac).subtract(bd).shiftLeft(N)).add(bd.shiftLeft(2*N));
}

public static void main(String[] args){
    System.out.println(karatsuba3(BigInteger.valueOf(12347634456346L), BigInteger.valueOf(23346345643565L)));
}
公共静态BigInteger karatsuba3(BigInteger x,BigInteger y){
//断绝暴力
int N=Math.max(x.bitLength(),y.bitLength());

如果(N)从另一个角度看,您正在使用新的而不经常删除。您绝对确定它在返回后被正确删除了吗?@Spektre:
我看不到biginger Ten=new biginger(“10”);被删除的位置
-这不会是由于JRE/JVM垃圾收集造成的原因(首先是没有删除)。不使用
java.math.BigInteger.TEN/TWO
是很奇怪的-对于适合
long
(且未预定义)的值,
BigInteger.valueOf(long val)
将是合适的。使用10(或其幂)对于divmod是有问题的。人们最多可以看一看
biginger
实现,经常在Karatsuba之上使用Toom Cook。@灰胡子,正如我提到的,我没有用JAVA编写代码,所以我不知道其行为。无论如何,除了学习目的之外,整个实现对于bigint
*
使用bigint是无效的
/、%
pow
也有问题,因为它限制了输入值。在实际实现中,使用的基本
WORD
s计数减半。基本值从来都不是
10
,而是
2
的幂或
10
的最大幂适合ALU
WORD
使用。这是将所有内容转换为指针转换或mem复制。我甚至没有注意到在
biginger.pow(int-power)
中使用位操作是合适的(将指数限制为int-不处理@Spektre建议的范围内的基,但不能(轻松)定义
biginger.pow(长幂)
(在JRE实现之外)。将
Ten.pow(n.divide(biginger.TWO))
(和
Math.pow(10,n/2)
)重构到BASE中。尽管它的名字很糟糕,但还是使用
biginger.divideandremainer()
。我太笨了。谢谢!@hsnsd只是一个输入错误,它发生了;)你能解释一下使用.xor和.pow的区别吗?@hsnsd你想做的是将输入的数字分成几乎相等长度的两半。这可以通过使用整数除法和10的适当幂的模除法来实现。你也可以使用位掩码来实现相同的结果你在@rustot answer?It com中使用了什么但它给出了错误的答案我试了12*12,结果111@hunter他将rustot的答案idk中的.xor改为.pow,如果这有什么不同的话。你用2000位以上的数字检查了吗?否则你的Karatsuba乘法就不用了…(因为你有一个
if
来表示)我可以看到
karatsuba3()
中的karatsuba3乘法代码没有从任何数字不超过2000位的调用中执行。我可以看到
karatsuba3()
没有直接递归调用
karatsuba3()
。它不起作用,因为我在其他地方使用了==而不是.equals。谢谢!(更糟糕的是,使用
=
可能会也可能不会给出使用
biginger.valueOf(4711)
的预期行为,至少在不同的JRE之间(就像
String
intern()
)和基本类型包装器一样)。.pow和.xor?xor()之间有什么区别是位掩码上的独占析取运算。即十。xor(1)是11,十。xor(2)是8
public static BigInteger karatsuba3(BigInteger x, BigInteger y) {

    // cutoff to brute force
    int N = Math.max(x.bitLength(), y.bitLength());
    if (N <= 2000) 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 b = x.shiftRight(N);
    BigInteger a = x.subtract(b.shiftLeft(N));
    BigInteger d = y.shiftRight(N);
    BigInteger c = y.subtract(d.shiftLeft(N));

    // compute sub-expressions
    BigInteger ac    = karatsuba3(a, c);
    BigInteger bd    = karatsuba3(b, d);
    BigInteger abcd  = karatsuba3(a.add(b), c.add(d));

    return ac.add(abcd.subtract(ac).subtract(bd).shiftLeft(N)).add(bd.shiftLeft(2*N));
}

public static void main(String[] args){
    System.out.println(karatsuba3(BigInteger.valueOf(12347634456346L), BigInteger.valueOf(23346345643565L)));
}