我想将BigInteger替换为Stream Api中的循环

时间:2019-07-11 05:58:19

标签: java performance java-stream

要替换Java Stream API中的BigInteger循环。

下面的代码,我必须使用Stream API在java8中进行修改。由于性能问题。 我的问题是将以下代码转换为最佳性能的最佳实践是什么。

public BigInteger getSum(BigInteger n, BigInteger c) {
    BigInteger sum = BigInteger.ZERO;
    for (BigInteger i = BigInteger.ZERO; i.compareTo(n) < 0; i=i.add(BigInteger.ONE)) {
        sum = sum.add(calculateProduct(i, i.subtract(BigInteger.ONE), c));
    }
    return sum;
}

private BigInteger calculateProduct(BigInteger i, BigInteger j, BigInteger c) {
    if (j.compareTo(BigInteger.ZERO) < 0) return BigInteger.ZERO;
    if (j.compareTo(BigInteger.ZERO) == 0) return BigInteger.ONE;
    if ((i.subtract(j).compareTo(c)) <= 0){
        return j.add(BigInteger.ONE).multiply(calculateProduct(i, j.subtract(BigInteger.ONE), c));
    }else
        return BigInteger.ONE;
}

2 个答案:

答案 0 :(得分:1)

您似乎正在添加宽度为c的滑动窗口的乘积。如果要提高性能,请避免递归并避免重新计算整个产品。使用在上一步中计算出的乘积:将其乘以进入窗口的数字,再除以离开窗口的数字。除法速度较慢,但​​会为较大的c带来回报。

最后,尽管我会保留您的方法签名,但实际上您只需要BigInteger作为返回。

BigInteger getSum(BigInteger n, BigInteger c) {
    BigInteger p = BigInteger.ONE, sum = BigInteger.ZERO;

    for (BigInteger x = BigInteger.ONE; x.compareTo(n) < 0; x = x.add(BigInteger.ONE)) {
        p = p.multiply(x);
        if (x.compareTo(c) > 0) {
            p = p.divide(x.subtract(c));
        }
        sum = sum.add(p);
    }

    return sum;
}

如果要进一步加快速度,可以使用一些数学运算来避免每一步都需要除法。您的总和可以分为s1 = sum[1,c) x!s2 = sum[c,n) x!/(x-c)!。第二个总和等于n!/(n-c-1)!/(c+1)(从hockey stick identity开始)。下面的方法不能处理c> = n的琐碎情况。我会留给你的。

private static BigInteger fasterSum(BigInteger n, BigInteger c) {
    assert c.compareTo(n) < 0;
    BigInteger sum = ZERO, p = ONE;

    for (BigInteger x = ONE; x.compareTo(c) < 0; x = x.add(ONE)) {
        p = p.multiply(x);
        sum = sum.add(p);
    }

    // sum[c,n) x!/(x-c)! = n!/(n-c-1)!/(c+1)
    p = ONE;
    for (BigInteger x = n.subtract(c); x.compareTo(n) <= 0; x = x.add(ONE)) {
        p = p.multiply(x);
    }
    sum = sum.add(p.divide(c.add(ONE)));

    return sum;
}

答案 1 :(得分:0)

所以我假设您想添加BigInteger的列表:
您可以使用减少操作(https://docs.oracle.com/javase/tutorial/collections/streams/reduction.html

    List<BigInteger> list = new ArrayList<>();
            list.add(BigInteger.valueOf(5));
            list.add(BigInteger.valueOf(1));
            list.add(BigInteger.valueOf(3));
            list.add(BigInteger.valueOf(10));
            list.add(BigInteger.valueOf(2));

    BigInteger sum = list.stream().reduce((x, y) -> x.add(y)).get();
    System.out.println(sum);

这将计算总和,等效值为:

BigInteger sum = list.stream().reduce(BigInteger::add).get();

您还可以编写可重用的自己的Collector,然后将reduce操作提取到其中:

public static Collector<BigInteger, ?, BigInteger> calculateSum() {
    return Collectors.reducing(BigInteger.ZERO, BigInteger::add);
}

然后执行:

BigInteger sum = list.stream().collect(calculateSum());