A combinations problem. Given N
and R
, find NCR
.
There’s a variety of ways; The first two solutions are straight-forward computation of
. The last two have some tricks:
A naive Java solution that uses big arithmetic:
import java.math.*;
import java.util.*;
public class Main {
private static BigInteger C(BigInteger n, BigInteger r) {
r = (r.compareTo(n.subtract(r)) > 0) ? n.subtract(r) : r;
BigInteger num = BigInteger.ONE, den = BigInteger.ONE;
while (!r.equals(BigInteger.ZERO)) {
num = num.multiply(n); den = den.multiply(r);
r = r.subtract(BigInteger.ONE); n = n.subtract(BigInteger.ONE);
}
return num.divide(den);
}
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
while (s.hasNext()) {
BigInteger n = s.nextBigInteger(), r = s.nextBigInteger();
if (n.equals(BigInteger.ZERO) && r.equals(BigInteger.ZERO))
break;
System.out.printf("%s things taken %s at a time is %s exactly.\n", n, r, C(n, r));
}
}
}
A similar C++ solution:
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
unsigned long long C(int n, int r) {
r = min(n - r, r);
long long out = 1;
for (int i = 1; i <= r; ++i) {
out *= n - r + i;
out /= i;
}
return out;
}
int main() {
long long n, r;
while (scanf("%lld %lld", &n, &r) == 2 && n && r)
printf("%lld things taken %lld at a time is %lld exactly.\n", n, r, C(n, r));
return 0;
}
A slower, but totally accepted, solution reduces the computations as you were doing it by hand; We have two arrays, one for the numerator and another for the denominator. We do a 2D loop to reduce the numerator with respect to the denominator and hence the answer. C++ implementation:
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
#define REDUCE(a, b) { int g = gcd(a, b); a /= g; b /= g; }
int num[100], den[100];
int numI, denI;
long long gcd(long long a, long long b) {
long long t;
while (a) { t = a; a = b % a; b = t; }
return b;
}
long long C(int n, int r) {
// fill arrays
r = min(n - r, r);
numI = denI = 0;
while (r) { num[numI++] = n--; den[denI++] = r--; }
// reduce & compute
long long out = 1;
for (int i = 0; i < numI; ++i) {
for (int j = 0; j < denI; ++j)
REDUCE(num[i], den[j]);
out *= num[i];
}
return out;
}
int main() {
long long n, r;
while (scanf("%lld %lld", &n, &r) == 2 && n && r)
printf("%lld things taken %lld at a time is %lld exactly.\n", n, r, C(n, r));
return 0;
}
The last solution has the same complexity as the first two. It uses Pascal’s Triangle for computing combinations. C++ implementation:
#include <cstdio>
using namespace std;
#define N 128
long long pascalTri[N * (N + 1) / 2];
void fillPascal() {
for (int l = 0, lStart = 0; l < N; ++l, lStart += l) {
pascalTri[lStart] = pascalTri[lStart + l] = 1;
for (int i = 1; i < l; ++i)
pascalTri[lStart + i] = pascalTri[lStart + i - l] + pascalTri[lStart + i - l - 1];
}
}
int main() {
fillPascal();
long long n, r;
while (scanf("%lld %lld", &n, &r) == 2 && n && r)
printf("%lld things taken %lld at a time is %lld exactly.\n", n, r, pascalTri[n * (n + 1) / 2 + r]);
return 0;
}