Search code examples
cfactorialpascals-triangle

Strange Calculation Error When Calculating "N Choose K" in C


I am writing a program that prints out the rows of pascals traingle, and it works up to the 14th row, which has the values for 13. I've narrowed down the issue to the Choose Function I have made to do "N Choose K", which seems to produce incorrect values after "12 Choose X" and I have no idea why.

Here is the code I have for both the function that calculates factorials, (which seems to work) and the one that has issues. Also included is a copy and paste of the triangle produced after the 14th Row.

Also, for reference, doing printf("%ld \n", choose(13, 1)); produces the result of 4. It should be 13.

long factorial(int value)
{
    int i;
    long running = 1;
    for (i = 1; i <= value; i++)
    {
        running *= i;
    }
    return running;
}

long choose(int n, int k)
{
    if (n < k)
        return 0; 

    return factorial(n) / (factorial(k) * factorial(n - k));
}

1 1 -4 -1 2 4 7 9 9 7 4 2 -1 -4 1 1

1 0 1 5 14 29 44 50 44 29 14 5 1 0 1

1 4 24 88 221 399 532 532 399 221 88 24 4 1 <--------- Where the issue starts.

1 12 66 220 495 792 924 792 495 220 66 12 1

1 11 55 165 330 462 462 330 165 55 11 1

1 10 45 120 210 252 210 120 45 10 1

1 9 36 84 126 126 84 36 9 1

1 8 28 56 70 56 28 8 1

1 7 21 35 35 21 7 1

1 6 15 20 15 6 1

1 5 10 10 5 1

1 4 6 4 1

1 3 3 1

1 2 1

1 1

1

Ive tried changing the types from Int to Long thinking it was a data issue, but this is not the case.


Solution

  • Factorial 13! will overflow a 32-bit integer, and 21! will overflow a 64-bit integer. There is a way round this, by working with a running term.

    Here is one way to output a row of the Pascal Triangle, with no factorials needed.

    #include <stdio.h>
    #include <stdlib.h>
    
    void PascalRow(int row)
    {
        long long term = 1;
        int multiplier = row;
        int divisor = 1;
        printf("1");
        for(int i=0; i<row; i++) {
            term = term * multiplier / divisor;
            printf(" %lld", term);
            multiplier--;
            divisor++;
        }
        printf("\n");
    }
    
    int main(int argc, char *argv[]) {
        if(argc < 2)
            return 1;
        PascalRow(atoi(argv[1]));   // note: improve this
        return 0;
    }
    

    Program sessions

    test 6
    1 6 15 20 15 6 1 
    

    and

    test 15
    1 15 105 455 1365 3003 5005 6435 6435 5005 3003 1365 455 105 15 1