Karatsuba算法错误

时间:2016-11-12 19:37:09

标签: algorithm karatsuba

我正在进行karatsuba实施,但我有这个错误:

java.lang.NumberFormatException: Zero length BigInteger

    at java.math.BigInteger.<init>(BigInteger.java:296)
    at java.math.BigInteger.<init>(BigInteger.java:476)
    at com.Karatsuba.resolve(Karatsuba.java:20)
    at com.Karatsuba.resolve(Karatsuba.java:26)
    at com.KaratsubaTest.karatsubaShouldMultiply(KaratsubaTest.java:22)

这是我的方法:

BigInteger resolve(BigInteger left, BigInteger right) {

        String leftS = left.toString();
        String rightS = right.toString();

        int digits = Math.max(leftS.length(), rightS.length());
        digits = (digits / 2) + (digits % 2);

        if (left.compareTo(new BigInteger("10", 10)) == -1 || right.compareTo(new BigInteger("10", 10)) == -1) {
            return left.multiply(right);
        }

        BigInteger firstLeft = new BigInteger(leftS.substring(0, digits));
        BigInteger secondLeft = new BigInteger(leftS.substring(firstLeft.toString().length(), leftS.length()));
        BigInteger firstRight = new BigInteger(rightS.substring(0, digits));
        BigInteger secondRight = new BigInteger(rightS.substring(firstRight.toString().length(), rightS.length()));

        BigInteger z2 = resolve(firstLeft, firstRight);
        BigInteger z0 = resolve(secondLeft, secondRight);
        BigInteger z1 = resolve(firstLeft.add(secondLeft), firstRight.add(secondRight)).subtract(z2).subtract(z0);

        return z2.multiply(BigInteger.valueOf((long) Math.pow(10, 2 * digits)))
                .add(z1.multiply(BigInteger.valueOf((long) Math.pow(10, digits))))
                .add(z0);
    }

我认为这是因为我使用不同长度的参数,例如123和456789.有什么想法吗?

1 个答案:

答案 0 :(得分:0)

NumberFormatException引发了new BigInteger(""),例如left = 10right = 100,从那时起你会得到这样的结果:

digits = 2
firstLeft = new BigInteger("10".substring(0,2)) = new BigInteger("10")
secondLeft = new BigInteger("10".substring(2,2)) = new BigInteger("")

这很容易通过检查字符串是否为空来纠正,如果是,则将数字设置为零,例如像这样

    BigInteger a = fromString(leftS.substring(0, digits));
    BigInteger b = fromString(leftS.substring(a.toString().length(),digits));
    BigInteger c = fromString(rightS.substring(0, digits));
    BigInteger d = fromString(rightS.substring(c.toString().length(), rightS.length()));

...

    private BigInteger fromString(String str) {
        if (str.length() == 0)
            return BigInteger.ZERO;
        else
            return new BigInteger(str);
    }

我尝试运行算法,虽然它运行但是(算法实现)本身仍然存在问题。例如,100 * 100报告为1000000.我认为这与分裂的发生方式有关,但我无法完全修复它。以下是一些反思:

如果你在m = 3时分裂ab = 12345,那么在代码中它会得到a = 123和b = 45。但如果您正在阅读维基百科文章(我做过),那么您需要

ab = a*10^m+b

而你有ab = a*10^(ab.length-m)+b

前者可以通过更改代码来轻松完成:

int l = leftS.length();
int r = rightS.length();
int m0 = Math.max(l, r);
int r0 = m0%2;
int m = (m0 / 2) + r0;

...

BigInteger a = fromString(leftS.substring(0, m-r0));
BigInteger b = fromString(leftS.substring(a.toString().length(),l));
BigInteger c = fromString(rightS.substring(0, m-r0));
BigInteger d = fromString(rightS.substring(c.toString().length(), r));

现在100 * 100 = 10000,但是当位数不同时仍然存在问题。我找不到什么是错的,可能必须真正经历算法的每一步(而不是仅仅复制维基百科伪代码)才能找到错误。希望这仍然有一些帮助!

顺便说一下,我认为算法可能会更好地使用base 2,因为那时你可以为乘法做位移。分裂也可能更容易(也更快)。此外,递归应该可以更快地停止,因为算法仅对非常大的数字有效。这些当然是优化,但我认为它们值得一提。