貪欲法と最適性の証明(交換論法・マトロイド)
貪欲法が「たまたま当たる」のか「必ず最適」なのかを見極める力が手に入る。交換論法とマトロイドで正当性を証明し、区間スケジューリングやハフマン符号で実際に確かめる。
- 1.貪欲法は各ステップで局所最良を選ぶだけだが、これが大域最適になるのは特定の構造を満たすときに限られる。
- 2.正当性の主軸は交換論法。最適解を貪欲解へ1手ずつ置換しても最適性を崩さないことを示し、貪欲解の最適を導く。
- 3.マトロイドという代数構造の上では「重みの大きい順に取る貪欲」が常に最適。区間スケジューリングやハフマン符号も交換論法で正当化できる。
貪欲法は「いつ」正しいのか
貪欲法(greedy algorithm)は、各ステップでその時点の局所最良を選び、一度選んだら後戻りしないアルゴリズム設計です。実装は単純で高速ですが、致命的な問題があります。局所最良の積み重ねが大域最適になる保証はない。たとえば最短経路でも、ナップサック問題(0-1版)でも、素朴な貪欲は簡単に最適を外します。
だからこそ上級者に問われるのは「貪欲が動くか」ではなく「なぜこの貪欲は最適なのか、証明できるか」です。正当性を支える道具は主に2つ――交換論法(exchange argument)とマトロイド理論です。漸近計算量の記法に不安があれば計算量と Big-O 記法を先に押さえてください。
貪欲が最適になるには、問題が2つの性質を持つ必要があります。
- 貪欲選択性(greedy choice property): ある局所最良の選択を含む大域最適解が必ず存在する。つまり最初の貪欲な一手を「捨てない」最適解が取れる。
- 最適部分構造(optimal substructure): 最初の選択を固定した後の残り問題の最適解を足すと、全体の最適解になる。
この2つが揃えば、最初の貪欲な選択を残り問題に再帰的に適用するだけで最適解に到達します。動的計画法が「すべての選択を試して最良を残す」のに対し、貪欲は「正しい一手が事前に分かっている」点が本質的な違いです。
交換論法: 最適解を貪欲解へ寄せる
交換論法は、貪欲選択性を証明する最も汎用的な手法です。骨子はこうです。
- 任意の最適解
OPTを取る。 OPTが貪欲の最初の選択gを含まないと仮定する。OPTの中の要素をgと**入れ替え(交換)**ても、解の価値が悪化しないことを示す。- ゆえに
gを含む最適解が存在する。あとは残り問題に帰納を回す。
ポイントは「交換しても最適性を壊さない」を1手ぶんだけ示せばよいことです。全体を一気に比較する必要はありません。具体例で見ます。
区間スケジューリング(互いに重ならないよう最大個数の区間を選ぶ)では、貪欲は「終了時刻が最も早い区間を選ぶ」を繰り返します。これが最適である証明が交換論法の典型です。
greedy_interval(intervals):
sort intervals by finish time ascending
selected = []
last_finish = -infinity
for iv in intervals:
if iv.start >= last_finish: # 直前と重ならない
selected.append(iv)
last_finish = iv.finish
return selected
証明: 貪欲が最初に選ぶ区間を g(最も早く終わる区間)とする。最適解 OPT の中で最も早く終わる区間を o とすると、g の終了時刻は o 以下。よって OPT の o を g に差し替えても、g は o より早く終わるので後続の区間と衝突せず、選べる区間数は減らない。これで g を含む最適解が作れた。残りの区間に同じ議論を繰り返せば、貪欲解が最適とわかる。
同じ区間スケジューリングでも、基準を「開始が早い順」「区間が短い順」「重なりが少ない順」に変えると最適性は崩れます。長い1区間が早く始まるだけで多数の短区間を潰す反例が作れるためです。貪欲は基準の選び方そのものに正当性が宿るのであって、貪欲という枠だけでは何も保証しません。
ハフマン符号: 交換で最適性を保証する
データ構造の応用であるハフマン符号も、交換論法で最適性が示せる代表例です。各文字に頻度が与えられ、平均符号長が最小の**接頭符号(prefix code)**を作る問題です。貪欲は「頻度が最小の2要素を併合して新しい節点にする」を繰り返し、二分木を下から組み上げます。
huffman(freqs):
Q = min-heap of nodes keyed by frequency
while size(Q) > 1:
a = pop_min(Q); b = pop_min(Q)
merge = Node(freq = a.freq + b.freq, left=a, right=b)
push(Q, merge)
return pop_min(Q) # 根
正当性の核は次の交換補題です。最小頻度の2文字 x, y は、ある最適符号木で必ず最も深い同じ親を持つ兄弟にできる。証明は、最適木で最も深い兄弟 a, b を取り、x と a、y と b を入れ替える。頻度が小さい要素を深い位置へ動かすので、平均符号長(頻度×深さの総和)は増えない。つまり最適性を保ったまま x, y を最深の兄弟にできる。あとは x, y を併合した縮小問題に帰納すれば、貪欲が最適接頭符号を生むと示せます。
ハフマンの「2つ併合して1つの仮想要素にする」操作は、最適部分構造そのものです。x と y を頻度 fx+fy の1要素に置き換えたより小さい問題の最適解に、最後の1ビット分岐を足すと元の最適解になる。貪欲選択(最小2つを併合)と最適部分構造が噛み合うからこそ、貪欲が成立します。
マトロイド: 貪欲が常に最適になる構造
交換論法は強力ですが、問題ごとに証明を作り直す手間があります。**マトロイド(matroid)**は「どんな重みでも貪欲が最適になる」問題クラスを代数的に特徴づける一般理論です。
マトロイドは集合 S とその部分集合族(独立集合)I の組で、次の3公理を満たします。
- 空集合は独立。
- 遺伝性: 独立集合の任意の部分集合も独立。
- 交換性: 独立集合
A, BでAがBより要素数が小さいなら、Bの中にAへ加えても独立なままの要素が必ず存在する。
この交換性こそ、交換論法を構造として埋め込んだものです。各要素に重みを付け、重みの大きい順に、独立性を壊さない要素を取る貪欲(マトロイド貪欲)は、最大重み独立集合を常に与えることが証明できます(Rado–Edmonds の定理)。
| 問題 | マトロイドの種類 | 独立集合の意味 |
|---|---|---|
| 最小全域木 | グラフ的マトロイド | 閉路を作らない辺集合(森) |
| 最大重みマッチング(横断) | 横断マトロイド | 相異なる相手に割当可能な要素集合 |
| 線形独立な列の選択 | 線形マトロイド | 一次独立なベクトル集合 |
最も身近な例が最小全域木のクラスカル法です。辺を重み昇順に見て、閉路を作らなければ採用する――これは「閉路を作らない辺集合=森」を独立集合とするグラフ的マトロイド上の貪欲そのもの。だから個別の交換論法を組まなくても、マトロイドの定理1本で最適性が従います。
「問題がマトロイドの形をしていれば貪欲は最適」は十分条件であって必要条件ではありません。区間スケジューリングやハフマンはマトロイドの枠に直接は乗りませんが、専用の交換論法で最適性が示せます。逆に 0-1 ナップサックは交換しても価値が悪化し得るため貪欲が最適にならず、動的計画法が要ります。「貪欲が使えるか」は交換が価値を悪化させないかを最終的な判定軸にしてください。
判断の手順をまとめる
貪欲法を実務やアルゴリズム設計で採るとき、正当性の確認は次の順で進めると曖昧さが消えます。
- まず反例を探す。素朴な貪欲基準で最適を外す入力を作れたら、その基準は捨てる。基準ごとに最適性が決まる。
- 交換論法を試す。最適解の1要素を貪欲の選択へ入れ替えても価値が悪化しないか。悪化しないなら貪欲選択性が立つ。
- マトロイドに乗るか確認する。独立集合が遺伝性と交換性を満たすなら、重み順貪欲が無条件で最適。クラスカル法はこの型。
- 乗らないなら動的計画法へ切り替える。最適部分構造はあるが貪欲選択性がない問題(0-1 ナップサックなど)は、全選択を試す DP が正解。
貪欲は「速いから使う」のではなく「最適性を証明できるから使う」設計です。交換論法という1手ぶんの議論と、マトロイドという構造の保証――この2つを押さえれば、貪欲が当たるか外れるかを勘でなく根拠で判断できます。
プログラミング Article
貪欲法と最適性の証明(交換論法・マトロイド)を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
アルゴリズム
比較で見る軸
難易度: advanced / カテゴリ: プログラミング / タグ数: 5
導入後に効く点
正当性の主軸は交換論法。最適解を貪欲解へ1手ずつ置換しても最適性を崩さないことを示し、貪欲解の最適を導く。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- プログラミング
- タグ数
- 5
判断チェックリスト
- 自社の用途が「アルゴリズム / 貪欲法」に近いか確認する。
- 強みである「貪欲法は各ステップで局所最良を選ぶだけだが、これが大域最適になるのは特定の構造を満たすときに限られる。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。