Day 081-Q5 — 拡張ラグランジュ法・ADMM(凸最適化の主双対反復)

2026-07-04 赤色 Master / Phase 8+ ★★★★★★★★★ ADMM・Augmented Lagrangian・LP 緩和・最大重み独立集合

問題

グラフ $G = (V, E)$($|V| = N$, $|E| = M$)が与えられ,各頂点 $i$ に重み $c_i > 0$ が付く。最大重み独立集合の LP 緩和を ADMM で解き,LP 最適値と整数解(丸め)の目的関数値を求めよ。

$$\text{maximize} \sum_{i \in V} c_i x_i \quad \text{s.t.} \quad x_i + x_j \le 1 \; \forall (i,j) \in E, \quad 0 \le x_i \le 1$$

制約

パラメータ範囲備考
$N$$2 \le N \le 200$頂点数
$M$$1 \le M \le N(N-1)/2$辺数
$c_i$$1 \le c_i \le 100$頂点重み

入出力例

入力例1

4 4
3 5 2 4
1 2
2 3
3 4
4 1

出力例1

LP: 9.00
Integer: 9

概念図: ADMM の 3 ステップ反復

ADMM(交互方向乗数法)— 主双対反復 標準形: $\min f(x) + g(z)$ s.t. $Ax = z$ $f(x) = -c^\top x + \delta_{[0,1]^N}(x)$, $g(z) = \delta_{\{z \le 1\}}(z)$, $A$ = 辺-頂点接続行列 Step 1: x-update $x^{k+1} = \text{clip}(M^{-1} r, 0, 1)$ $M = A^\top A + \rho I$ $r = c + \rho A^\top(z-u)$ Step 2: z-update $z^{k+1} = \min(Ax^{k+1} + u, 1)$ 辺制約 $z \le 1$ への射影 (element-wise min) Step 3: u-update(双対) $u^{k+1} = u^k + Ax^{k+1} - z^{k+1}$ 残差の累積 → 制約を徐々に満たす 収束まで繰り返し(通常 500〜2000 回) 収束判定: primal residual $\|Ax - z\| < \varepsilon$, dual residual $\|\rho A^\top(z^{k+1}-z^k)\| < \varepsilon$ $\rho$ の適応的調整($\rho$ が大 → 収束速いが精度低下,$\rho$ が小 → 遅い)

ヒント

ヒント1(方向性)

ADMM は制約付き最適化を「x の更新」「補助変数 z の更新」「双対変数 u の更新」の 3 ステップに分解する。凸 LP に対して最適解に収束する。

ヒント2(アプローチ)

LP 標準形: $\max c^\top x$, s.t. $Ax \le 1$, $0 \le x \le 1$ を,$z$ を補助変数として $\min -c^\top x$, s.t. $Ax = z, z \le 1, 0 \le x \le 1$ に変換。

  • x-update: $(A^\top A + \rho I)x = c/\rho + A^\top(z-u)$ を解いて $[0,1]$ にクリップ
  • z-update: $z = \min(Ax + u, 1)$($z \le 1$ への射影)
  • u-update: $u = u + Ax - z$(残差の累積)
ヒント3(ほぼ答え)
import numpy as np
rho = 2.0
A = ...  # (M, N) 辺-頂点接続行列
M_mat = A.T @ A + rho * np.eye(N)
M_inv = np.linalg.inv(M_mat)
x = np.zeros(N); z = np.ones(M); u_dual = np.zeros(M)

for _ in range(2000):
    rhs = c + rho * A.T @ (z - u_dual)
    x = np.clip(M_inv @ rhs, 0.0, 1.0)
    z = np.minimum(A @ x + u_dual, 1.0)
    u_dual = u_dual + A @ x - z

模範解答

import sys
import numpy as np

def solve():
    data = sys.stdin.read().split()
    idx = 0
    N = int(data[idx]); idx+=1
    M = int(data[idx]); idx+=1
    c = np.array([float(data[idx+i]) for i in range(N)]); idx+=N
    edges = []
    adj = [[] for _ in range(N)]
    for _ in range(M):
        u, v = int(data[idx])-1, int(data[idx+1])-1; idx+=2
        edges.append((u, v)); adj[u].append(v); adj[v].append(u)

    if M == 0:
        lp_val = float(sum(c))
        print(f"LP: {lp_val:.2f}")
        print(f"Integer: {int(lp_val)}")
        return

    A = np.zeros((M, N))
    for k, (u, v) in enumerate(edges):
        A[k, u] = 1.0; A[k, v] = 1.0

    rho = 2.0
    M_mat = A.T @ A + rho * np.eye(N)
    M_inv = np.linalg.inv(M_mat)

    x = np.zeros(N); z = np.ones(M); u_dual = np.zeros(M)

    for it in range(2000):
        x_prev = x.copy()
        rhs = c + rho * A.T @ (z - u_dual)
        x = np.clip(M_inv @ rhs, 0.0, 1.0)
        z = np.minimum(A @ x + u_dual, 1.0)
        u_dual = u_dual + A @ x - z
        if np.max(np.abs(x - x_prev)) < 1e-8: break

    lp_val = float(c @ x)

    # 整数解: x 値降順に貪欲選択
    sel = np.zeros(N, dtype=bool)
    for i in np.argsort(-x):
        if all(not sel[nb] for nb in adj[i]):
            sel[i] = True

    int_val = int(c @ sel)
    print(f"LP: {lp_val:.2f}")
    print(f"Integer: {int_val}")

solve()

Step-by-Step 解説

Step 1: ADMM の基本形

問題: $\min f(x) + g(z)$, s.t. $Ax + Bz = c$

Augmented Lagrangian: $L_\rho = f(x) + g(z) + y^\top(Ax+Bz-c) + \frac{\rho}{2}\|Ax+Bz-c\|^2$

3 ステップの交互更新で $L_\rho$ を最小化。凸問題に対して収束が保証される。

Step 2: LP 緩和への適用

辺制約 $x_i + x_j \le 1$ を補助変数 $z$ に持たせる。x-update は $[0,1]$ ボックス制約への射影(クリップ),z-update は $z \le 1$ への射影(element-wise min)。

Step 3: $M^{-1}$ の前計算

x-update の線形方程式 $(A^\top A + \rho I)x = r$ は毎回同じ左辺 → $M^{-1} = (A^\top A + \rho I)^{-1}$ を事前計算することで各 iter が $O(N^2)$ に。

Step 4: 整数解の丸め戦略

LP 最適値 $x^*$ から整数解を構築:$x_i \ge 0.5$ の頂点を降順に選び,隣接頂点が選ばれていなければ採用(貪欲)。

よくあるミス

ミス原因正しい書き方
M_mat が特異孤立頂点などrho * np.eye(N) で常に正定値 (rho > 0)
z-update の範囲ミス$z \ge 0$ も必要と誤解辺制約 $z \le 1$ のみ → np.minimum(..., 1.0)
丸めで独立集合が壊れる丸め後に辺チェック不足選択順を x 値降順にして逐次チェック

次のステップ

  • 発展問題: SDP(半正定値計画)+ ADMM による MAX-CUT 近似解法
  • 参考: CVXPY(Python の凸最適化ライブラリ)で同問題を解いて ADMM 解と比較
  • $\rho$ の適応的調整(Primal/Dual Residual バランシング)

自己評価

理解度: / /

自分の回答:

気づき・メモ: