日々drdrする人のメモ

今日も明日もdrdr

グラフのchain decompositionで橋と関節点を求める

グラフのchain decompositionと、それを使って橋と関節点を求める手法のメモ

橋と関節点を求める手法といえばlowlinkを用いた手法が存在するが、他の手法としてchain decompositionを用いた手法が存在する。(WikipediaBridge (graph theory)Biconnected componentに載っている)

以下を参考にしている
[1] Schmidt, Jens M. "A simple test on 2-vertex-and 2-edge-connectivity." Information Processing Letters 113.7 (2013): 241-244.

chain decompositionとは

chain decomposition はDFS-treeをベースにグラフをpathもしくはcycle (= chain)に分けた上で、2-edge-connectivity(1つの辺を除去しても連結)もしくは2-connectivity(1つの頂点を除去しても連結)を判定する線形時間のアルゴリズム

この chain decomposition では ear decomposition を計算しており、グラフが 2-edge-connected であれば ear decomposition となり、2-connected であれば open ear decomposition となる。

この判定を利用し、橋もしくは関節点を列挙することができる。

構築方法

グラフGのchain decompositionは以下のようにして求める。

1. グラフGを根付き木としてDFSし、DFS-treeを求める

  • この時、treeに含まれる辺をtree edge、含まれない辺をbackedgeとする
  • tree edgeを根頂点に向かう有向辺、backedgeを根頂点から離れる有向辺とする

2. DFSで訪ねた頂点をpre-orderの順序で見ていき、各頂点を始点とする各backedgeに対するchainを求める
chainは以下のように求める (現在見ている頂点をvとする)

  • 頂点vが始点となる各backedgeについて、そのbackedgeの終点の頂点wからtree edgeを伝って頂点vへと遡っていく
    • この時、各頂点にはvisitフラグをつけておく
  • 到達した頂点に既にvisitフラグが立っているか、頂点vに到達した場合は探索を停止
  • 頂点wから探索を停止した頂点xまでのpath(もしくは頂点vまで探索できた場合のcycle)を1つのchainとする

この時、 i番目に見たbackedgeから求めたchainを C_iとする

3. 各backedgeから求めたchainの集合をchain decomposition  {C = \{C_1, C_2, C_3, ..., C_K\}}とする

  • chainは {K = |E| - |V| + 1}個(backedgeの数だけ)存在する

例として以下のグラフを考える。

f:id:smijake3:20190615160847p:plain
例として使うグラフ

このグラフ上で一番上の頂点からDFSを行い、tree edgeとbackedgeを得る。以下の図の黒い辺がtree edge、赤い辺がbackedgeである。頂点についている番号はpre-orderの順序。

f:id:smijake3:20190617011011p:plain
グラフのDFS-tree

そして、このDFSのpre-orderの順序で各backedgeに対するchainを得る。

f:id:smijake3:20190617011050p:plain
得られるchain decomposition

このchain decompositionは  {O(|V| + |E|)} で得ることができる。

橋の列挙

このchain decompositionがグラフ Gの辺の分割(partition)となる場合、2-edge-connectedであると判定できる。

2-edge-connectedでない場合は橋が存在し、この橋は任意のchain  {C_i}に含まれていない辺を列挙することで求められる。

例のグラフにおける橋はchain decompositionに含まれていない、以下の図で橙丸で囲った辺が該当する。

f:id:smijake3:20190617011123p:plain
グラフ上の橋

関節点の列挙

グラフ Gの全頂点の次数が2以上であり、かつchain decompositionに含まれるcycleが C_1のみである場合、グラフは2-connectedであると判定できる。

2-connectedでない場合は関節点が存在し、関節点は以下のいずれかを満たす次数が2以上の頂点が該当する。(次数1の頂点は除去しても非連結にならない)

1. 橋となる辺に隣接する頂点
2.  {C_1}以外でcycleとなるchain  {C_i}の、探索を開始したbackedgeの始点となる頂点 {v}

例のグラフでは、橋に隣接する頂点2, 9と cycleのchain  C_2, C_3 のbackedgeの始点である頂点3, 6が関節点になることが分かる。橋に隣接する頂点10は次数1であるため、関節点にはならない。

f:id:smijake3:20190617011205p:plain
グラフ上の関節点

実装

連結グラフ {G}に対する橋と関節点を計算する。

# chain decomposition の構築
def construct(G, N):
    # P[v]: DFS-treeにおける頂点vの親頂点
    P = [0]*N
    # G0: グラフGのbackedgeのみに絞った有向グラフ
    G0 = [[] for i in range(N)]
    # V: 頂点をpre-orderに並べたもの
    V = []

    # P, V, G0 を計算するDFS
    lb = [0]*N
    def dfs(v, p):
        P[v] = p
        V.append(v)
        lb[v] = len(V)
        for w in G[v]:
            if w == p:
                continue
            if lb[w]:
                if lb[v] < lb[w]:
                    # (v, w) は backedge
                    G0[v].append(w)
                continue
            # (v, w) は tree edge
            dfs(w, v)
    dfs(0, -1)

    # B: 橋となる辺e = (u, v) のリスト
    B = []
    # ap[v]: 頂点vが関節点であるか?
    ap = [0]*N

    used = [0]*N
    first = 1
    used[0] = 1
    # グラフの頂点をpre-orderで見ていく
    for u in V:
        if not used[u]:
            # 頂点uは以前に探索されてない
            # -> この親頂点pへのtree edgeがchainとして含まれない
            # -> 辺(u, p) は橋 
            p = P[u]
            B.append((u, p) if u < p else (p, u))
            # 橋に隣接し、次数が2以上の頂点は関節点
            if len(G[u]) > 1:
                ap[u] = 1
            if len(G[p]) > 1:
                ap[p] = 1

        # 頂点vが始点となるbackedgeについて調べる
        cycle = 0
        for v in G0[u]:
            # tree edgeに従って根頂点に向かって上がっていく
            w = v
            while w != u and not used[w]:
                used[w] = 1
                w = P[w]
            # このchainはcycle
            if w == u:
                cycle = 1

        if cycle:
            if not first:
                # 2つ目以降のcycle chainである場合、頂点uは関節点
                ap[u] = 1
            first = 0

    A = [v for v in range(N) if ap[v]]
    return B, A

# B: グラフGの橋
# A: グラフGの関節点
B, A = construct(G, N)
Verified