17

How can you calculate large factorials using C#? Windows calculator in Win 7 overflows at Factorial (3500). As a programming and mathematical question I am interested in knowing how you can calculate factorial of a larger number (20000, may be) in C#. Any pointers?

[Edit] I just checked with a calc on Win 2k3, since I could recall doing a bigger factorial on Win 2k3. I was surprised by the way things worked out.

  1. Calc on Win2k3 worked with even big numbers. I tried !50000 and I got an answer, 3.3473205095971448369154760940715e+213236

  2. It was very fast while I did all this.

The main question here is not only to find out the appropriate data type, but also a bit mathematical. If I try to write a simple factorial code in C# [recursive or loop], the performance is really bad. It takes multiple seconds to get an answer. How is the calc in Windows 2k3 (or XP) able to perform such a huge factorial in less than 10 seconds? Is there any other way of calculating factorial programmatically in C#?

Rahul Soni
  • 4,941
  • 3
  • 35
  • 58
  • 3
    Three (or is it two? dunno) language-agnostic words: Arbitrary-precision arithmetic. –  Nov 15 '10 at 16:17
  • 2
    Is this `[homework]` or `[interview-questions]`? – Bobby Nov 15 '10 at 16:18
  • 2
    The Windows calculator has a maximum value of 10^10,000, meaning 3248! is the highest factorial it can calculate. – Gabe Nov 15 '10 at 16:24
  • 2
    20000! is a ~260000 bit integer. That's a big integer. What are you doing with such big numbers? – Eric Lippert Nov 15 '10 at 16:37
  • 1
    Remember if you have something like n!/r! where n > r then you only need to calculate `n * (n-1) * (n-2) * ... * (r+2) * (r+1)` – Callum Rogers Nov 15 '10 at 17:47
  • Hi Eric, I was checking some interview questions, and felt intrigued by this question. I have edited my main question to add some more details. – Rahul Soni Nov 16 '10 at 03:10

10 Answers10

20

Have a look at the BigInteger structure:

http://msdn.microsoft.com/en-us/library/system.numerics.biginteger.aspx

Maybe this can help you implement this functionality.

CodeProject has an implementation for older versions of the framework at http://www.codeproject.com/KB/cs/biginteger.aspx.

Pieter van Ginkel
  • 29,160
  • 8
  • 71
  • 111
12

If I try to write a simple factorial code in C# [recursive or loop], the performance is really bad. It takes multiple seconds to get an answer.

Let's do a quick order-of-magnitude calculation here for a naive implementation of factorial that performs n multiplications. Suppose we are on the last step. 19999! is about 218 bits. 20000 is about 25 bits; we'll assume that it is a 32 bit integer. The final multiplication therefore involves the addition of up to 25 partial results each roughly 218 bits long. The number of bit operations will therefore be on the order of 223.

That's for the last stage; there will be 20000 = 216 such operations at each stage, so that is a total of about 239 operations. Some of them will of course be cheaper, but we're going for an order of magnitude here.

A modern processor does about 232 operations per second. Therefore it will take about 27 seconds to get the result.

Of course, the big integer library writers were not naive; they take advantage of the ability of the chip to do many bit operations in parallel. They're probably doing the math in 32 bit chunks, giving speedups of a factor of 25. So our total order-of-magnitude calculation is that it should take about 22 seconds to get a result.

22 is 4. So your observation that it takes a few seconds to get a result is expected.

How is the calc in Windows 2k3 (or XP) able to perform such a huge factorial in less than 10 seconds?

I don't know. Extreme cleverness in exploiting the math operations on the chip probably. Or, using a non-naive algorithm for calculating factorial. Or, possibly they are using Stirling's Approximation and getting an inexact result.

Is there any other way of calculating factorial programmatically in C#?

Sure. If all you care about is the order of magnitude then you can use Stirling's Approximation. If you care about the exact value then you're going to have to compute it.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 2
    Interestingly, Mathematica computes an exact answer for 22000! in under 1s on my machine. The [implementation notes](http://reference.wolfram.com/mathematica/tutorial/SomeNotesOnInternalImplementation.html#5107) page states that they use a prime decomposition together with the Schönhage–Strassen algorithm to compute the result. – LBushkin Nov 17 '10 at 20:29
10

There exist sophisticated computational algorithms for efficiently computing the factorials of large, arbitrary precision numbers. The Schönhage–Strassen algorithm, for instance, allows you to perform asymptotically fast multiplication for arbitrarily large integers.

Case in point, Mathematica computes 22000! on my machine in less than 1 second. The Implementation Notes page at reference.wolfram.com states:

(Mathematica's) n! uses an O(log(n) M(n)) algorithm of Schönhage based on dynamic decomposition to prime powers.

Unfortunately, the implementation of such algorithms is both complicated and error prone. Rather than trying to roll your own implementation, it may be wiser for you to license a copy of Mathematica (or a similar product that meets your functional and performance needs) and either use it, or a .NET programming interface to it, to perform your computation.

LBushkin
  • 129,300
  • 32
  • 216
  • 265
5

Have you looked at System.Numerics.BigInteger?

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
5

Using System.Numerics BigInteger

var bi = new BigInteger(1);
var factorial = 171;
for (var i = 1; i <= factorial; i++)
{
    bi *= i;
}

will be calculated to

1241018070217667823424840524103103992616605577501693185388951803611996075221691752992751978120487585576464959501670387052809889858690710767331242032218484364310473577889968548278290754541561964852153468318044293239598173696899657235903947616152278558180061176365108428800000000000000000000000000000000000000000

For 50000! it takes a couple seconds to calculate but it seems to work and the result is a 213237 digit number and that's also what Wolfram says.

Jonas Elfström
  • 30,834
  • 6
  • 70
  • 106
1

You will probably have to implement your own arbitrary precision numeric type.

There are various approaches. probably not the most efficient, but perhaps the simplest is to have variable length arrays of byte (unsigned char). Each element represents a digit. ideally this would be included in a class, and you can then add a method which let's you multiply the number with another arbitrary precision number. A multiply with a standard C# integer would probably also be a good idea, but a little trickier to implement.

winwaed
  • 7,645
  • 6
  • 36
  • 81
1

You need a special big-number library for this. This link introduces the System.Numeric.BigInteger class, and incidentally has an example program that calculates factorials. But don't use the example! If you recurse like that, your stack will grow horribly. Just write a for-loop to do the multiplication.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
TonyK
  • 16,761
  • 4
  • 37
  • 72
1

Since they don't give you the result down to the last digit, they may be "cheating" using some approximation. Check out http://mathworld.wolfram.com/StirlingsApproximation.html Using Stirling's formula you can calculate (an approximation of) the factorial of n in logn time. Of course, they might as well have a dictionary with pre-calculated values of factorial(n) for every n up to one million, making the calculator show the result extremely fast.

r0u1i
  • 3,526
  • 6
  • 28
  • 36
  • If you were to store 1M factorials, do you know what type of data would you use to store them individually. Even Int64 would not be enough, right? – Joan Venge Nov 17 '10 at 18:59
  • If you want to store them up to the last digit, then I guess you are right (you need something bigger than Int1000000). However, it seems that windows' calculator doesn't store them that way and probably use a floating point representation. – r0u1i Nov 19 '10 at 06:43
1

This answer covers limits for basic .Net types to compute and represent n!

Basic code to calculate factorial for "SomeType" that supports multiplication:

SomeType factorial = 1;
int n = 35;
for (int i = 1; i <= n; i++)
{
   factorial *= i; 
}

Limits for built in number types:

  • short - correct results up to 7!, incorrect results afterwards, code returns 0 starting 18 (similar to int)
  • int - correct results up to 12!, incorrect results afterwards, code returns 0 starting at 34 (Why computing factorial of realtively small numbers (34+) returns 0)
  • float - precise results up to 14!, correct but not precise afterwards, returns infinity starting at 35
  • long - correct results up to 20!, incorrect results afterwards, code returns 0 starting at 66 (similar to int)
  • double - precise results up to 22!, correct but not precise afterwards, returns infinity starting at 171
  • BigInteger - precise and upper limit is set by memory usage only.

Note: integer types overflow pretty quickly and start producing incorrect results. Realistically if you need factorials for any practical usage long is the type to go (up to 20!), if you can't expect limited numbers - BigInteger is the only type provided in .Net Framework to provide precise results (albeit slow for large numbers as there is no built-in optimized n! method)

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
0

I don't know how you could do this in a language without arbitrary precision arithmetic. I guess a start could be to count factors of 5 and 2, removing them from the product, and add on these zeroes at the end.

As you can see there are many.

>>> factorial(20000)
<<non-zeroes removed>>0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000L
jon_darkstar
  • 16,398
  • 7
  • 29
  • 37