长数组的确切总和

时间:2015-06-07 16:42:19

标签: java sum integer-arithmetic

为了获得long[]我使用以下代码段的确切总和。

public static BigInteger sum(long[] a) {
    long low = 0;
    long high = 0;
    for (final long x : a) {
        low += (x & 0xFFFF_FFFFL);
        high += (x >> 32);
    }
    return BigInteger.valueOf(high).shiftLeft(32).add(BigInteger.valueOf(low));
}

通过处理分成两半的数字并最终组合部分和,它可以正常工作。令人惊讶的是,这种方法也有效:

public static BigInteger fastestSum(long[] a) {
    long low = 0;
    long high = 0;
    for (final long x : a) {
        low += x;
        high += (x >> 32);
    }
    // We know that low has the lowest 64 bits of the exact sum.
    // We also know that BigInteger.valueOf(high).shiftLeft(32) differs from the exact sum by less than 2**63.
    // So the upper half of high is off by at most one.
    high >>= 32;
    if (low < 0) ++high; // Surprisingly, this is enough to fix it.
    return BigInteger.valueOf(high).shiftLeft(64).add(BigInteger.valueOf(low));
}

认为fastestSum应该按原样运行 。我相信它可以奏效,但在最后一步还有更多工作要做。但是,它通过了我的所有测试(包括大型随机测试)。所以我问:有人可以证明它有效或找到反例吗?

2 个答案:

答案 0 :(得分:4)

fastestSum(new long[]{+1, -1})  => -18446744073709551616

答案 1 :(得分:1)

这似乎有效。鉴于我的测试错过了我的琐碎版本的问题,我不确定它是否正确。谁想要分析这个是值得欢迎的:

public static BigInteger fastestSum(long[] a) {
    long low = 0;
    long control = 0;
    for (final long x : a) {
        low += x;
        control += (x >> 32);
    }
    /*
     We know that low has the lowest 64 bits of the exact sum.
     We also know that 2**64 * control differs from the exact sum by less than 2**63.
     It can't be bigger than the exact sum as the signed shift always rounds towards negative infinity.
     So the upper half of control is either right or must be incremented by one.
     */
    final long x = control & 0xFFFF_FFFFL;
    final long y = (low >> 32);
    long high = (control >> 32);
    if (x - y > 1L << 31) ++high;
    return BigInteger.valueOf(high).shiftLeft(64).add(BigInteger.valueOf(low));
}

它可能比理智的版本快30%。