簡體   English   中英

我想在 Stream Api 中替換 BigInteger for 循環

[英]I want to replace BigInteger for loop in Stream Api

想要替換 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;
}

看起來您正在添加寬度為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!/(xc)! . 第二個總和等於n!/(nc-1)!/(c+1) (來自曲棍球棒標識)。 下面的方法不處理 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;
}

所以我假設你想添加一個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並將減少操作提取到那里:

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

然后做:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM