Showing posts with label factorial. Show all posts
Showing posts with label factorial. Show all posts

Saturday, August 12, 2023

Nifty Info on 123456789 Factorial

The answers provided in the questions were collected using:

  • Default eclipse 2023-06 IDE for Mac
  • No special java vm args
  • jdk 1.8
  • Mac Studio M1 (10 core)

Question : How many digits are there in 123456789 factorial?

Answer : 260,959,261 digits.

Question : How long does it take to compute 123456789 factorial?

  • Answer 1 : Using prime sieve method, it takes 28,244,345 ms.
  • Answer 2 : Using recursive method, StackOverflowError.

Question : If the result of 123456789 factorial was written to a plain text file, how big will it be?

Answer : 263.6 MB.

Here's the full java code:

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class FactorialUsingPrimeSieveMethod {

    private int findExponent(int input, int prime) {
        int q = input, m = 0;
        if (prime > input) {
            return 0;
        }
        if (prime > input / 2) {
            return 1;
        }
        while (q >= prime) {
            q /= prime;
            m += q;
        }
        return m;
    }

    private boolean[] initializeSieve(int input) {
        boolean sieve[] = new boolean[input + 1];
        Arrays.fill(sieve, true);

        for (int p = 2; p * p <= input; p++) {
            if (sieve[p] == true) {
                for (int i = p * p; i <= input; i += p) {
                    sieve[i] = false;
                }
            }
        }

        return sieve;
    }

    private int[] selectPrimeNumbers(int input) {
        ArrayList<Integer> primeTable = new ArrayList<Integer>();
        boolean sieve[] = initializeSieve(input);
        for (int i = 2; i <= input; i++) {
            if (sieve[i] == true) {
                primeTable.add(i);
            }
        }

        return primeTable.stream().mapToInt(i -> i).toArray();

    }

    private int[] generateExponentTable(int input, int[] primeTable) {
        ArrayList<Integer> exponentTable = new ArrayList<Integer>();
        for (int prime : primeTable) {
            exponentTable.add(Integer.valueOf(findExponent(input, prime)));
        }

        return exponentTable.stream().mapToInt(i -> i).toArray();

    }

    private LinkedList<BigInteger> runParallelExponentiation(int[] primeTable, int[] exponentTable) {
        int proc = Runtime.getRuntime().availableProcessors();
        ExecutorService executorService = Executors.newFixedThreadPool(proc);

        List<Future<BigInteger>> futureList = new ArrayList<Future<BigInteger>>();
        LinkedList<BigInteger> intermediate = new LinkedList<BigInteger>();

        for (int i = 0; i < primeTable.length; i++) {
            Callable<BigInteger> worker = new PrimeSieveExponentiationWrapper(primeTable[i], exponentTable[i]);
            Future<BigInteger> submit = executorService.submit(worker);
            futureList.add(submit);
        }

        for (Future<BigInteger> future : futureList) {
            try {
                intermediate.add(future.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }

        executorService.shutdown();
        return intermediate;
    }

    private synchronized BigInteger runParallelMultiplication(LinkedList<BigInteger> intermediate) {
        BigInteger one = BigInteger.ONE;
        int proc = Runtime.getRuntime().availableProcessors();
        ExecutorService executorService = Executors.newFixedThreadPool(proc);

        final LinkedList<BigInteger> roundaboutList = intermediate;

        while (roundaboutList.size() != 1) {
            System.out.println(" Current size = " + roundaboutList.size());
            BigInteger x = intermediate.pollLast();
            System.out.println(" Size after first poll = " + roundaboutList.size());
            BigInteger y = intermediate.pollLast();
            System.out.println(" Size after second poll = " + roundaboutList.size());
            Callable<BigInteger> worker = new PrimeSieveMultiplicationWrapper(x, y);
            Future<BigInteger> submit = executorService.submit(worker);
            try {
                roundaboutList.addFirst(submit.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }

        executorService.shutdown();
        return one.multiply(roundaboutList.getFirst());
    }

    private BigInteger factorial(int input) {
        BigInteger result = BigInteger.ONE;
        int[] primeTable = selectPrimeNumbers(input);
        int[] exponentTable = generateExponentTable(input, primeTable);

        final LinkedList<BigInteger> intermediate = runParallelExponentiation(primeTable, exponentTable);
        BigInteger product = runParallelMultiplication(intermediate);
        return result.multiply(product);
    }

    private void exportToFile(BigInteger result, int n) {
        String toParse = result.toString();
        String parsedResult = toParse.replaceAll("(.{100})", "$1\n");
        String path = System.getProperty("user.home") + File.separator + "results" + File.separator + String.valueOf(n)
                + ".txt";

        try {
            File fileName = new File(path);
            if (!fileName.exists()) {
                fileName.getParentFile().mkdirs();
                fileName.createNewFile();
            }
            FileWriter writer = new FileWriter(fileName);
            writer.write(parsedResult);
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void recordExecTime(FactorialUsingPrimeSieveMethod calculator, int n) {
        long startTime = System.currentTimeMillis();
        BigInteger result = calculator.factorial(n);
        long endTime = System.currentTimeMillis();
        System.out.println(n + "! took " + (endTime - startTime) + " ms using prime sieve method. Result has = "
                + result.toString().length() + " digits");
        //exportToFile(result, n);
    }

    public static void main(String[] args) {
        FactorialUsingPrimeSieveMethod calculator = new FactorialUsingPrimeSieveMethod();
        calculator.recordExecTime(calculator, 12345);
        calculator.recordExecTime(calculator, 123456);
        calculator.recordExecTime(calculator, 1234567);
        calculator.recordExecTime(calculator, 12345678);
        calculator.recordExecTime(calculator, 123456789);
    }

    class PrimeSieveExponentiationWrapper implements Callable<BigInteger> {

        int prime;
        int exponent;

        public PrimeSieveExponentiationWrapper(int prime, int exponent) {
            this.prime = prime;
            this.exponent = exponent;
        }

        @Override
        public BigInteger call() throws Exception {
            // System.out.println(" Calculating " + prime + " ^ " + exponent);
            return BigInteger.valueOf(prime).pow(exponent);
        }
    }

    class PrimeSieveMultiplicationWrapper implements Callable<BigInteger> {

        private BigInteger x;
        private BigInteger y;

        PrimeSieveMultiplicationWrapper(BigInteger x, BigInteger y) {
            this.x = x;
            this.y = y;
        }

        @Override
        public BigInteger call() throws Exception {
            if (x == null) {
                if (y == null) {
                    return BigInteger.ONE;
                } else {
                    return y;
                }
            }
            return x.multiply(y);
        }
    }
}