简体   繁体   中英

calculating factorial using Java 8 IntStream?

I am relatively new in Java 8 and lambda expressions as well as Stream , i can calculate factorial using for loop or recursion. But is there a way to use IntStream to calculate factorial of a number ? I am fine even with factorial in integer range.

I read through IntStream docs here, http://docs.oracle.com/javase/8/docs/api/java/util/stream/IntStream.html and i can see so many methods but not sure which one I can use to calculate factorial.

for example, there is rang method that says,

range(int startInclusive, int endExclusive) Returns a sequential ordered IntStream from startInclusive (inclusive) to endExclusive (exclusive) by an incremental step of 1.

so I can use it to provide the range of numbers to IntStream to be multiplied to calculate factorial.

number = 5;
IntStream.range(1, number)

but how to multiply those numbers to get the factorial ?

You can use IntStream::reduce for this job,

int number = 5;
IntStream.rangeClosed(2, number).reduce(1, (x, y) -> x * y)

To get a stream of all infinite factorials, you can do:

class Pair{
   final int num;
   final int value;

    Pair(int num, int value) {
        this.num = num;
        this.value = value;
    }

}

Stream<Pair> allFactorials = Stream.iterate(new Pair(1,1), 
                                   x -> new Pair(x.num+1, x.value * (x.num+1)));

allFactorials is a stream of factorials of number starting from 1 to ..... To get factorials of 1 to 10:

allFactorials.limit(10).forEach(x -> System.out.print(x.value+", "));

It prints: 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800,

Now say you only wish to have a factorial of a particular number then do:

allFactorials.limit(number).reduce((previous, current) -> current).get()

The best part is that you dont recompute again for new numbers but build on history.

With LongStream.range() you can calculate factorial for number less 20. If you need calculate for larger number create stream with BigInteger:

 public BigInteger factorial(int number) {
    if (number < 20) {
        return BigInteger.valueOf(
                LongStream.range(1, number + 1).reduce((previous, current) -> previous * current).getAsLong()
        );
    } else {
        BigInteger result = factorial(19);
        return result.multiply(Stream.iterate(BigInteger.valueOf(20), i -> i.add(BigInteger.ONE)).limit(number - 19)
                .reduce((previous, current) -> previous.multiply(current)).get()
        );
    }
}

We can solve the question with AtomicInteger like this:

  int num = 5;
  AtomicInteger sum = new AtomicInteger(1);
  IntStream.rangeClosed(2, num).forEach(i -> {
    sum.updateAndGet(v -> v * i);
        if (i == num) {
          System.out.println(sum.get());
        }
  });

I think we can change the main condition to: 1,v v-1,s t with the function reduce. Maybe we can filter before we did the main rule

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM