I want to replace BigInteger for loop in Stream Api

Hamesh :

Want to replace BigInteger loop in Java Stream API.

Below code, I have to modify in java8 using Stream API. Because of performance concerns. My question what would be the best practice to change below code into best performance.

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;
}
Misha :

It looks like you are adding up products of sliding window of width c. If you want to improve performance, get rid of recursion and avoid recomputing the entire product. Use the product computed in the previous step: multiply it by the number entering the window and divide by the number exiting the window. Division is slower but it will pay off for larger values of c.

Finally, while I will keep your method signature, you really only need BigInteger as return.

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;
}

If you want to speed it up further, you can use a bit of math to avoid having to divide at every step. Your sum can be broken up into s1 = sum[1,c) x! and s2 = sum[c,n) x!/(x-c)!. The second sum equals n!/(n-c-1)!/(c+1) (follows from hockey stick identity). The method below does not handle the trivial case of c >=n. I will leave that to you.

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;
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=155228&siteId=1