組み合わせ
コード例
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
vector<vector<ll>> comb;
// パスカルの三角形を利用して組み合わせを前計算する
void init_nCk(int n) {
  n++;  // nCkを計算するにはn+1の領域が必要
  // n*nの領域を確保
  comb.resize(n, vector<ll>(n, 0));
  // パスカルの三角形の上端は1 (0C0)
  comb[0][0] = 1;
  for (int i = 1; i < n; i++) {
    // どの行も左端と右端は1になる nC0 = nCn = 1
    comb[i][0] = 1;
    comb[i][i] = 1;
    // 端以外の場所の組み合わせ数は、自身の左上と右上の組み合わせ数を足したものになる
    for (int j = 1; j < n - 1; j++) {
      comb[i][j] = (comb[i - 1][j - 1] + comb[i - 1][j]);
    }
  }
}
ll nCk(int n, int k) { return comb[n][k]; }
// nC2はよく利用するので単独で定義
ll nC2(ll n) { return n * (n - 1) / 2; }
int main() {
  init_nCk(4);
  cout << nCk(3, 2) << endl;  // 3
  cout << nCk(4, 4) << endl;  // 1
}
パスカルの三角形 を利用した組み合わせ計算関数。
init_nCk(n) で  ~  までの組み合わせ数を前計算しておき、実際の利用は nCk(n, k) で行う。
解説/アルゴリズム
異なる n 個のものから異なる r 個を選んで作った集合をn個からr個とった組み合わせといい、組み合わせの総数を  で表す。
たとえば a, b, c, d という 4 つの文字の中から 3 つの文字を選んで集合を作る場合、
{a, b, c}, {a, c, d}, {b, c, d}, {a, b, d}
この 4 つの集合を作ることができるので、 になる。
順列との違い
順列の場合、取り出す順番を区別するので、例えば上記の{a, b, c}という組み合わせに対して、
{a, b, c}, {a, c, b}, {b, a, c}, {b, c, a}, {c, a, b}, {c, b, a}
の 6 通りの順列がある。
この 6 通りの 6 は 3 x 2 x 1 、つまり 3! であり、 3 つの文字を選んだので順列は 3! になる。
他の組み合わせ 3 通りに対しても、それぞれ 6 通りの順列があることになるので、組み合わせと順列の関係は、
両辺を 3!で割ると、
計算してみると、
一般化すると次のようになる。
変形
n 個のものから r 個選ぶということは、他の n - r 個を残す、つまり残す n - r 個を選ぶということと同じなので、次の等式が成り立つ。
これは便利な変形で、たとえば を求める場合、 に変形して、結果が 1 とすぐにわかるので、複雑な計算をしなくて済む。
という風に、組み合わせの数は山型になるので、 の r が自由に選べるとき、一番大きい数になるのは となる。