6

Hello I currently have this piece of code for finding factorial which is work fine

public static BigInteger factorial(BigInteger n) {
    BigInteger sum = BigInteger.ONE;
    for (BigInteger i = BigInteger.ONE; i.compareTo(n) <= 0; i = i.add(BigInteger.ONE)) {
        sum = sum.multiply(i);
    }
    return sum;
}

What I want to achieve is to convert this to a Stream<BigInteger> and write it like this

public static BigInteger factorial(BigInteger n) {
    return getBigIntegerStream(n).reduce(BigInteger.ONE, BigInteger::multiply);
}

So my question is how I can get a Stream<BigInteger> similar to how I can declare an IntStream?

IntStream.range(1, myInt);
Stefan Zobel
  • 3,182
  • 7
  • 28
  • 38
Panos K
  • 1,061
  • 12
  • 26

3 Answers3

4

The equivalent would be

Stream.iterate(BigInteger.ONE, i -> i.add(BigInteger.ONE))
    .takeWhile(i -> i.compareTo(end) < 0)

where end is a BigInteger.

Stream.iterate will create an infinite stream, starting from 1 and continually adding 1. takeWhile will stop the stream once the condition is met.

Michael
  • 41,989
  • 11
  • 82
  • 128
  • https://stackoverflow.com/questions/20746429/limit-a-stream-by-a-predicate – Michael Jul 10 '19 at 10:02
  • When using Java 9+, you can use `Stream.iterate(BigInteger.ONE, bi -> bi.compareTo(n) < 0, BigInteger.ONE::add)` in the first place instead of resorting to `takeWhile`. – Holger Jul 11 '19 at 15:35
3

Perhaps something like this:

public static BigInteger factorial(BigInteger n) {
    return Stream.iterate (BigInteger.ONE, i -> i.add(BigInteger.ONE)).limit(Integer.parseInt(n.toString())).reduce(BigInteger.ONE, BigInteger::multiply);
}

EDIT: I forgot to limit the stream. Fixed now.

Of course it would be simpler to just accept an int (or a long) as the argument:

public static BigInteger factorial(int n) {
    return Stream.iterate (BigInteger.ONE, i -> i.add(BigInteger.ONE)).limit(n).reduce(BigInteger.ONE, BigInteger::multiply);
}

It is very unlikely you will even need to calculate the factorial of a number larger than Integer.MAX_VALUE. The factorial of such a number would be huge, and would probably take very long to calculate.

EDIT: Not a proper benchmark, but factorial(100000) took me 5 seconds, and factorial(1000000) took 8 minutes. At this rate, factorial(Long.MAX_VALUE) or even factorial(Integer.MAX_VAULE) will take very very long time. Therefore I don't see the point of requiring a BigInteger argument.

Eran
  • 387,369
  • 54
  • 702
  • 768
  • 1
    This break on Integer.parseInt when biginteger is bigger that a regular int which is the reason i dont use integers – Panos K Jul 10 '19 at 09:46
  • @PanosK Well, if you want to calculate the factorial of a number larger than `Integer.MAX_VALUE`, you'll get into trouble, but I doubt you'll even need to calculate such a huge value. – Eran Jul 10 '19 at 09:47
  • you are correct i finally used the your second version no need for BigInteger input, thanks – Panos K Jul 10 '19 at 11:00
  • It might be unlikely to ever want/need to calculate with more than `Integer.MAX_VALUE` elements, but still, there is no reason to restrict the number to `int` when `limit` accepts a `long`. And instead of `Integer.parseInt(n.toString())` , we can use the much cheaper `n.longValueExact()`. Also keep in mind that summands can be swapped, so instead of `i -> i.add(BigInteger.ONE)` you can also use `BigInteger.ONE::add`. Starting with Java 9, we can use `Stream.iterate(BigInteger.ONE, bi -> bi.compareTo(n) < 0, BigInteger.ONE::add)` which supports value beyond `Long.MAX_VALUE`—in theory. – Holger Jul 11 '19 at 15:33
1

You could try something like this:

public static BigInteger factorial(int n) 
{ 
  return IntStream.rangeClosed(1,n)
                  .mapToObj(BigInteger::valueOf)
                  .reduce(BigInteger::multiply)
}
cigien
  • 57,834
  • 11
  • 73
  • 112