1

I need to calculate a very large factorial, however it must be exact. I can't use an approximation.

I want to get 1,000,000,000!, but it's pretty slow. So far I have improved performance a bit, but its still not enough. Here is what I have:

        BigInteger Factor100 = BigInteger.One;
        BigInteger Factor10000 = BigInteger.One;
        Status = "Factorising";
        for (i++; i <= StartN; i++)
        {
            if (Worker.CancellationPending)
            {
                e.Cancel = true;
                break;
            }

            if (i % 10000 == 0)
            {
                Factor100 = Factor100 * i;
                Factor10000 = Factor10000 * Factor100;
                iFactorial = iFactorial * Factor10000;
                Factor100 = BigInteger.One;
                Factor10000 = BigInteger.One;
            }
            else if (i % 100 == 0)
            {
                Factor100 = Factor100 * i;
                Factor10000 = Factor10000 * Factor100;
                Factor100 = BigInteger.One;
            }
            else
            {
                Factor100 = Factor100 * i;
            }

            //iFactorial = i * iFactorial;

            if (i % Updates == 0)
            {
                Worker.ReportProgress(50, new Tuple<string, BigInteger>("Factorialising", i));
                using (StreamWriter DropWriter = File.CreateText(@FileLocation + "FactorialDropCatcher.dat"))
                {
                    DropWriter.WriteLine("N: " + i);
                    DropWriter.WriteLine("N!: " + iFactorial);
                }
            }
        }

So, I tried to stay away from calculating the insanely large numbers until it became necessary, keeping the running Factorial number updated only once every 10,000.

How could I calculate this faster?

pingu2k4
  • 956
  • 2
  • 15
  • 31
  • This is rather mathematical than programming issue and you didn't explain what you've done (pre-calculated factorials perhaps?). Displaying progress is expensive, you already reduce it. – Sinatr Jul 26 '16 at 09:26
  • `1000000000! = 9.90462...38144` (`8565705522` digits) with just `249999998` trailing zeroes. If you remove trailing zeroes you'll have (at best) `8565705522 - 249999998 == 8315705524` - **3 per cent** improvement – Dmitry Bychenko Jul 26 '16 at 09:33
  • 2
    Why do you want *exact* factorial value? *Stirling formula* provides a reasonable approximation: https://en.wikipedia.org/wiki/Stirling%27s_approximation – Dmitry Bychenko Jul 26 '16 at 09:36
  • @Sinatr I simply have a way of providing it the values I saved from last time, so I can give it a value for i and iFactorial which was saved out to the text file previously. Otherwise, they are both equal to 1 by this point. DmitryBychenko I need it to be the exact number, as stated in OP. Reason being is the calculations I plan on performing with this depend upon it being the exact number. – pingu2k4 Jul 26 '16 at 09:43
  • What kind of calculations? Perhaps explaining the problem [here](http://math.stackexchange.com/) will help you to get optimized formula, which will not require to perform that many iterations. You seems over-estimating accuracy. – Sinatr Jul 26 '16 at 09:48
  • With such a large number you would definitely benefit from parallel processing. Have you looked into that? – Timo Salomäki Jul 26 '16 at 09:50
  • Is the maximal input bounded? If so, you might want to go with pre-computed values. Maybe not all of them but at least some at which you can start. Another thing is, try to minimize the total amount of numerals that are used in your computations. I haven't put that much thought into it, but it might be advantageous to compute those using something akin to a balanced tree with the numbers as leaves. Although if doing so, an eye has to be kept on memory complexity. – Aziuth Jul 26 '16 at 10:08
  • @Dmytri: trailing zeroes make the BigIntegers larger, and making them smaller by reducing zeroes can asymptotically increase performance. Anyway, the larger the numbers get, the slower the calculations will be. – Rudy Velthuis Jul 26 '16 at 13:07
  • Ever thought of doing a divide and conquer? You divide 1 billion up in smaller parts, calculate them separatley by dividng those up again, etc. In other words, the BigInt version of http://stackoverflow.com/questions/13657265/why-is-the-divide-and-conquer-method-of-computing-factorials-so-fast-for-large – Rudy Velthuis Jul 26 '16 at 13:13
  • @Sinatr: The number of iterations is not the problem. The problem is that the result gets larger and larger, so when you progress, you are multiplying ever larger numbers, which means you are getting slower and slower. If you can divide those huge numbers up in smaller and calculate these recursively, that means you only have to multiply a few very large values, and not drag along all the rest all the time. That is *divide and conquer*, and is done for many algorithms already. You will have a few more iterations, but most of them with much smaller values. – Rudy Velthuis Jul 26 '16 at 13:36

1 Answers1

0

For this, I just use an extension method for IEnumerable<int>.Product(). It's like IEnumerable<int>.Sum(), but the product. For a factorial of N, just create a range from 1 up to N and take its product.

This is surprisingly fast, and if your number crunching needs are rather extreme, modifying it to use PLINQ is a snap!

public class FactorialExample
{
    public static BigInteger Factorial(int n)
    {
        return Enumerable.Range(2, n).Product();
    }    
}

public static class IEnumerableExtensionMethods
{
    public static BigInteger Product(this IEnumerable<int> multiplicands)
    {
        System.Numerics.BigInteger result = 1;
        foreach (int multiplier in multiplicands)
        {
            result = System.Numerics.BigInteger.Multiply(result, multiplier);
        }
        return result;
    }
}
Adam White
  • 3,180
  • 1
  • 21
  • 18