Reputation: 23
My code is doing well when I enter small numbers like 10
choose 2
, but when it comes to 50
choose 10
, its result is wrong, can you tell me what's wrong here?
#include <stdio.h>
long long int factorial(int n);
long long int combn(int n, int k);
int main(void) {
int n = 0;
int k = 0;
printf("Enter n and k:\n");
scanf("%d %d", &n, &k);
combn(n, k);
}
long long int combn(int n, int k) {
long long int C = 0;
C = factorial(n) / (factorial(k) * factorial(n - k));
printf("C %d choose %d = %ld\n", n, k, C);
}
long long int factorial(int n) {
if (n == 1)
return 1;
else
return n * factorial(n - 1);
}
combn(50, 10)
should be 10272278170
.
Upvotes: 1
Views: 58
Reputation: 144923
Computing n
choose k
for moderately large values of n
with the default formula involves intermediary results that exceed the range of type long long
. There are 2 solutions to address this issue:
Here is a modified version with the latter approach:
#include <limits.h>
#include <stdio.h>
unsigned long long int combn(int n, int k) {
if (k < 0 || n < 0 || k > n)
return 0;
// minimize computations
if (k > n - k)
k = n - k;
int factors[k];
// initialize factors of n! / (n - k)!
for (int i = 0; i < k; i++)
factors[i] = n - i;
for (int i = k; i > 1; i--) {
// find the multiple of i, divide it by i
for (int j = 0; j < k; j++) {
if (factors[j] % i == 0) {
factors[j] /= i;
break;
}
}
}
// compute result
unsigned long long int C = 1;
for (int i = 0; i < k; i++) {
if (C > ULLONG_MAX / factors[i])
return ULLONG_MAX;
C = C * factors[i];
}
return C;
}
int main(void) {
int n, k;
printf("Enter n and k: ");
if (scanf("%d %d", &n, &k) == 2) {
unsigned long long C = combn(n, k);
if (C == ULLONG_MAX)
printf("overflow\n");
else
printf("%d choose %d is %llu\n", n, k, C);
}
return 0;
}
Output:
50 choose 10 is 10272278170
Upvotes: 0
Reputation: 4732
you overflow the long long
capacity (and you code is mixing long long
and int
BTW)
you need to compute 50! which is ~ 3.10^64, thus far above max int
(~2^9) and max long long int
which is ~9.10^18.
You need to use a special big integer library or rework your algo not to compute overflowed values (or do not use big values...)
Seems there are algorithm that can compute combination that fits a long long without overflowing; see: Calculating the Amount of Combinations
Upvotes: 1
Reputation: 108736
50!
is a very large number, taking almost 150 bits to represent, The long long
datatype provides only 64 bits. So, C can't do the computation the way you're doing it; it overflows.
You can use an arbitrary-precision arithmetic package library for this purpose. This kind of library represents numbers with variable numbers of bits, and offers operations that don't overflow.
gmp -- the Gnu MP Bignum library, is an example of such a library. There are others. Here's how you might do it with gmp. (not debugged).
#include "gmp.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int main(int argc, char * argv[]){
uint n;
uint m;
mpz_t nn;
mpz_t mmn;
mpz_t mmm;
mpz_t denom;
mpz_t result;
char * str;
if (argc <= 2){
printf ("Usage: %s <number> <number> \n", argv[0]);
return 1;
}
n = atoi(argv[1]);
m = atoi(argv[2]);
mpz_fac_ui (nn,n); /* nn = n! */
mpz_fac_ui (mmn,n-m); /* mmn = (n-m)! */
mpz_fac_ui (mmm,m); /* mmm = m! */
mpz_mul(denom, mmm, mmn); /* denom = mmn * mmm */
mpz_fdiv_q(result, nn, denom); /* result = nn / denom */
str = mpz_get_str (null, 10, const mpz_t result);
printf ("deal %d from %d: %s combinations\n", n,m, str);
free (str);
mpz_clear(nn);
mpz_clear(mmm);
mpz_clear(mmn);
mpz_clear(denom);
mpz_clear(result);
return 0;
}
Another possibility: take advantage of the fact that (n!) / (n-m)!
is equal to the product of the integers from (m+1 to n). For example 50!/ 47!
is 48 * 49 * 50
. That should, in many cases, keep your integers representable in 64 bits. And, even better when you're doing this kind of computer arithmetic, you don't have to perform an actual division operation because it falls right out of the formulas.
Upvotes: 1