C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++で二項係数を計算する方法:再帰と動的計画法による実装

二項係数とは

二項係数は C(n, k) または ⁿCᵣ と表記され、(1+X)ⁿ を二項展開したときの xk の係数として定義されます。

また、二項係数は「n 個の要素の中から k 個を選ぶ方法の総数」、すなわち n 要素の集合から取り出す k 個の組み合わせ(k-combination)の数も表します。選ぶ順序は考慮されない点に注意してください。

この記事では、パラメータ n と k が与えられたときに、二項係数 ⁿCₖ の値を返すプログラムを C++ で実装します。

入力 : n = 8, k = 3
出力 : 56

この問題には複数の解法が存在します。以下では、代表的な2つのアプローチを紹介します。

解法1:再帰呼び出しによる一般的な解法

二項係数は、パスカルの三角形の性質に基づく次の漸化式を用いて再帰的に計算できます。

c(n, k) = c(n-1, k-1) + c(n-1, k)

c(n, 0) = c(n, n) = 1

この公式は「n 番目の要素を選ぶ場合と選ばない場合の数を足し合わせる」という考え方に基づいています。これを C++ で実装すると次のようになります。

実装例

#include <iostream>
using namespace std;
int binomialCoefficients(int n, int k) {
    if (k == 0 || k == n)
        return 1;
    return binomialCoefficients(n - 1, k - 1) + binomialCoefficients(n - 1, k);
}
int main() {
    int n = 8, k = 5;
    cout << "The value of C(" << n << ", " << k << ") is " << binomialCoefficients(n, k);
    return 0;
}

出力

The value of C(8, 5) is 56

この再帰的な解法はシンプルで直感的に理解しやすい反面、同じ部分問題が何度も計算されるため、計算量は指数オーダーで増大します。そのため、n や k が大きくなると実行時間が急激に伸びるという欠点があります。

解法2:動的計画法(DP)による効率的な解法

再帰解法では部分問題の重複(overlapping subproblems)が発生します。そこで、動的計画法を用いて計算済みの結果を配列に保存し、再計算を避けることで効率化できます。

以下の実装では、1 次元配列を使い、パスカルの三角形を 1 行ずつ右から更新していくことで、空間計算量を O(k) に抑えています。

実装例

#include <bits/stdc++.h>
using namespace std;
int binomialCoefficients(int n, int k) {
    int C[k + 1];
    memset(C, 0, sizeof(C));
    C[0] = 1;
    for (int i = 1; i <= n; i++) {
        for (int j = min(i, k); j > 0; j--)
            C[j] = C[j] + C[j - 1];
    }
    return C[k];
}
int main() {
    int n = 8, k = 5;
    cout << "The value of C(" << n << ", " << k << ") is " << binomialCoefficients(n, k);
    return 0;
}

出力

The value of C(8, 5) is 56

まとめ

二項係数の計算には、シンプルな再帰解法と動的計画法の 2 つのアプローチがあります。再帰解法はコードが短く理解しやすい一方で、計算量が指数オーダーになるため大きな入力には不向きです。一方、動的計画法を用いれば時間計算量 O(n×k)、空間計算量 O(k) で効率的に求められます。実用上は動的計画法による実装が推奨されます。

  1. C++で学ぶ二項ヒープのメモリ表現 ― 二項木の構造とノードの5つのフィールド

    二項木(Binomial Tree)とは? 二項木とは、順序木データ構造の一種です。最小の二項木 B0 は単一のノードで構成され、一般に Bk で表される二項木は、2つの Bk-1 の二項木を連結したものになります。このとき、片方の二項木の根は、もう片方の二項木の根の左端の子として接続されます。この性質により、次数 k の二項木は必ず 2k 個のノードを持ちます。 なお、「二項木」という名前は金融分野のオプション価格評価(二項モデル)でも登場しますが、本記事で扱うのはデータ構造としての二項木・二項ヒープであり、主に優先度付きキューの効率的な実装に活用されます。 二項ヒープ(Binomial H

  2. C++で学ぶ二項ヒープ(Binomial Heap)の基礎と操作

    二項ヒープ(Binomial Heap)とは、二分ヒープ(Binary Heap)を拡張したデータ構造です。二分ヒープが提供する各種操作に加えて、より高速なマージ(union)操作を実現できる点が大きな特徴です。二項ヒープは、複数の二項木(Binomial Tree)のコレクションとして表現されます。二項木(Binomial Tree)とは?次数kの二項木は、次数k-1の二項木を2つ用意し、一方をもう一方の最左の子として連結することで構築できます。次数kの二項木には、以下のような性質があります。ノードの総数は正確に2k個である。木の深さはkである。深さi(i = 0, 1, ..., k)には