Search code examples
javafactorial

How can I parallelize this factorial code to be more efficient?


I have written this piece of code for computing the factorial of any number that it starts to become slow when the input is about one million or higher.

How can I parallelize it to be more efficient?

I haven't studied parallelization of programs.

package programas;

import java.math.BigInteger;
import java.util.InputMismatchException;
import java.util.Scanner;

public class IterativeFactorial {

  public BigInteger factorial(BigInteger n) {
    if ( n == null ) {
      throw new IllegalArgumentException();
    }
    else if ( n.signum() == - 1 ) {
      // negative
      throw new IllegalArgumentException("Argument must be a non-negative integer");
    }
    else {
      BigInteger factorial = BigInteger.ONE;
      for ( BigInteger i = BigInteger.ONE; i.compareTo(n) < 1; i = i.add(BigInteger.ONE) ) {
        factorial = factorial.multiply(i);
      }
      return factorial;
    }
  }

  public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    BigInteger number, result;
    boolean error = false;
    System.out.println("FACTORIAL OF A NUMBER");
    do {
      System.out.println("Enter a number:");
      try {
        number = scanner.nextBigInteger();
        result = new IterativeFactorial().factorial(number);
        error = false;
        System.out.println("Factorial of " + number + ": " + result);
      }
      catch ( InputMismatchException e ) {
        error = true;
        scanner.nextLine();
      }

      catch ( IllegalArgumentException e ) {
        error = true;
        scanner.nextLine();
      }
    }
    while ( error );
    scanner.close();
  }

}

Solution

  • You can use Stream to achieve easy parallelization of the computations by using reduction.

    A reduction operation (also called a fold) takes a sequence of input elements and combines them into a single summary result by repeated application of a combining operation, such as finding the sum or maximum of a set of numbers, or accumulating elements into a list.

    Above is from the package summary, take a read for more in-depth information.

    BigInteger num = scanner.nextBigInteger();
    BigInteger result = Stream.iterate(BigInteger.TWO, bi -> bi.compareTo(num) <= 0, bi -> bi.add(BigInteger.ONE))
            .parallel()
            .reduce(BigInteger.ONE, BigInteger::multiply);
    System.out.println(result);
    
    1. Stream.iterate() will create a stream equivalent to looping from 2 to input number inclusive. Starting from 2, because 1 * 1 = 1, no point in doing this operation.
    2. parallel() will parallelize the stream, otherwise it will be sequential, same as the loop.
    3. reduce() will reduce the stream to a single value, starting from 1, by multiplying all elements.