Day 025-Q4 — Wilson's Algorithm (LERW) + 有効抵抗

2026-05-08 赤色 Master / Phase 8+ ★★★★★★★★★ URST / Laplacian Pseudo-inverse

問題

無向連結グラフ $G$ から Wilson's Algorithm で一様ランダムスパニングツリーを生成。$K$ 回試行で辺頻度を計測し、ラプラシアン擬似逆行列を用いた有効抵抗の理論値と比較する。

制約

$2 \le N \le 15$
$N - 1 \le M \le N(N-1)/2$
$1 \le K \le 1000$

入出力例

入力例 1

3 3 1000
1 2
2 3
1 3
12345

出力例 1

(理論的に各辺の有効抵抗 ≈ 2/3)

ヒント (段階的開示)

ヒント1: 方向性
Wilson: Loop-Erased Random Walk でツリー外頂点を順次取り込む。期待 $O(N \cdot t_{cover})$。
ヒント2: アプローチ
有効抵抗 $R_{eff}(u,v) = L^\dagger_{uu} + L^\dagger_{vv} - 2 L^\dagger_{uv}$ で計算。numpy.linalg.pinv を使用。
ヒント3: 誘導
辺 $(u,v)$ が URST に含まれる確率 = $R_{eff}(u, v)$(Kirchhoff の対応)。

模範解答 (Python)

import sys
import random
import numpy as np
input = sys.stdin.readline

def main():
    N, M, K = map(int, input().split())
    edges = []
    adj = [[] for _ in range(N)]
    adj_matrix = [[False] * N for _ in range(N)]
    for _ in range(M):
        u, v = map(int, input().split())
        u -= 1; v -= 1
        edges.append((u, v))
        adj[u].append(v); adj[v].append(u)
        adj_matrix[u][v] = adj_matrix[v][u] = True
    seed = int(input())
    rng = random.Random(seed)

    def wilson(rng_instance):
        in_tree = [False] * N
        next_node = [-1] * N
        tree_edges = set()
        in_tree[0] = True
        for i in range(1, N):
            if in_tree[i]: continue
            v = i
            while not in_tree[v]:
                nxt = rng_instance.choice(adj[v])
                next_node[v] = nxt
                v = nxt
            v = i
            while not in_tree[v]:
                in_tree[v] = True
                u = next_node[v]
                tree_edges.add((min(v, u), max(v, u)))
                v = u
        return tree_edges

    sample_tree = wilson(rng)
    print(f"Sample URST (seed={seed}):")
    edge_strs = " ".join(f"({u+1},{v+1})" for u, v in sorted(sample_tree))
    print(f"Edges: {edge_strs}")
    print()

    edge_to_idx = {(min(u,v), max(u,v)): i for i, (u, v) in enumerate(edges)}
    freq = [0] * M
    for _ in range(K):
        tree = wilson(rng)
        for e in tree:
            if e in edge_to_idx:
                freq[edge_to_idx[e]] += 1

    L = np.zeros((N, N))
    for i in range(N):
        for j in range(N):
            if adj_matrix[i][j]:
                L[i][j] = -1.0
                L[i][i] += 1.0
    Lpinv = np.linalg.pinv(L)
    def R_eff(u, v):
        return Lpinv[u][u] + Lpinv[v][v] - 2 * Lpinv[u][v]

    print(f"Edge frequencies over {K} trials:")
    for i, (u, v) in enumerate(edges):
        print(f"({u+1},{v+1}): {freq[i]/K:.3f} (theoretical: {R_eff(u,v):.3f})")
    print()
    print("Effective resistances:")
    for u, v in edges:
        print(f"({u+1},{v+1}): {R_eff(u,v):.4f}")

main()

Step-by-Step 解説

1Wilson の正当性
LERW で各 URST が等確率。Aldous-Broder より高速。
2LERW
next_node[v] をランダム選択で上書き → ループ自然消去。
3有効抵抗
ラプラシアン $L = D - A$(特異)の擬似逆行列で $R_{eff}$ を計算。$\sum R_{eff} = N - 1$(Kirchhoff)。
4計算量
Wilson は $O(N \cdot t_{cover})$ 期待。$\text{pinv}$ は $O(N^3)$。

よくあるミス

ミス原因正しい書き方
有向辺扱い無向で両方向に追加が必要adj[u].append(v); adj[v].append(u)
next_node 未更新無限ループ毎回上書き
L の逆行列を使用L は特異np.linalg.pinv(L)

次のステップ

  • Aldous-Broder との収束速度比較
  • 代数的連結度 (Fiedler 値) と有効抵抗

自己評価

自分の回答

気づき・メモ