6  グラフ理論

6.1 学習目標

  • グラフ,点,辺の定義を理解する.
  • 無向グラフと有向グラフの違いを理解する.
  • 次数,出次数,入次数を求めることができる.
  • パスとその長さの定義を理解する.
  • 有向非巡回グラフ(DAG)の定義を理解し,トポロジカルオーダーを求めることができる.

6.2 グラフとは

グラフ(graph)は,点(vertex)の集合 V と辺(edge)の集合 E から構成され,G=(V,E) で表される.

グラフは,無向グラフ(undirected graph)と有向グラフ(directed graph)に分けられる.

6.3 無向グラフ

無向グラフは,辺に方向がないグラフである.辺は,2つの点からなる集合として表される.

例 6.1 (無向グラフの例) 点の集合 V と辺の集合 E を

\begin{aligned} V &= \{v_1, v_2, v_3, v_4\}, \\ E &= \{\{v_1,v_2\},\{v_2,v_3\},\{v_3,v_4\},\{v_4,v_1\},\{v_1,v_3\}\} \end{aligned}

とすると,G=(V,E) は無向グラフである.図 6.1 はこのグラフを表している.

コード
import networkx as nx
import matplotlib.pyplot as plt


def draw_graph(G, pos):
    """グラフ G を座標 pos に従って描画する.

    有向グラフの場合は,辺を矢印で表す.
    """
    # 点のラベルを LaTeX 形式に変換
    labels = {i: rf"$v_{i}$" for i in G.nodes()}
    arrow_options = {"arrowstyle": "-|>", "arrowsize": 12} if G.is_directed() else {}

    fig, ax = plt.subplots(figsize=(5, 3.5))
    nx.draw_networkx(
        G,
        pos,
        labels=labels,
        node_color="white",
        edgecolors="black",
        linewidths=1.0,
        node_size=450,
        font_size=11,
        width=1.0,
        ax=ax,
        **arrow_options,
    )
    ax.set_aspect("equal")
    ax.margins(0.1)
    ax.axis("off")
    plt.show()


# グラフの作成(点を番号で定義)
G = nx.Graph()
G.add_edges_from([(1, 2), (2, 3), (3, 4), (4, 1), (1, 3)])

# 点の座標を固定
pos = {1: (0, 1), 2: (1.4, 1), 3: (1.4, 0), 4: (0, 0)}
draw_graph(G, pos)
図 6.1: 無向グラフの例

グラフ G = (V, E) において,e = \{v, u\} \in E のとき,点 v と u は隣接(adjacent)しているといい,辺 e は v と u に接続(incident)しているという.

点 v に接続している辺の数を,v の次数(degree)という.例 6.1 の場合,v_1 の次数は 3,v_2 の次数は 2 である.

点 v に隣接する点を v の隣接点(neighbor)という.点 v の隣接点の集合 \mathcal{N}(v) は,

\mathcal{N}(v) = \{u \in V \mid \{v, u\} \in E\}

で定義される.例 6.1 において,\mathcal{N}(v_1) = \{v_2, v_3, v_4\} である.

6.4 有向グラフ

有向グラフは,辺に方向があるグラフである.辺は,2つの点の順序対として表される.

例 6.2 (有向グラフの例) 点の集合 V と辺の集合 E を

\begin{aligned} V &= \{v_1, v_2, v_3, v_4\}, \\ E &= \{(v_1,v_2),(v_2,v_3),(v_3,v_4),(v_4,v_1),(v_1,v_3)\} \end{aligned}

とすると,G=(V,E) は有向グラフである.図 6.2 はこのグラフを表している.

コード
# グラフの作成(点を番号で定義)
G = nx.DiGraph()
G.add_edges_from([(1, 2), (2, 3), (3, 4), (4, 1), (1, 3)])

# 点の座標を固定(無向グラフの例と同じ配置)
pos = {1: (0, 1), 2: (1.4, 1), 3: (1.4, 0), 4: (0, 0)}
draw_graph(G, pos)
図 6.2: 有向グラフの例

有向グラフ G = (V, E) において,点 v から出る辺の数を出次数(outdegree)といい,点 v に入る辺の数を入次数(indegree)という.例 6.2 の場合,v_1 の出次数は 2,入次数は 1 である.

辺 (v, u) において,u を v の後続点(successor),v を u の先行点(predecessor)という. 点 v の後続点の集合 \mathcal{S}(v) と先行点の集合 \mathcal{P}(v) は, \mathcal{S}(v) = \{u \in V \mid (v, u) \in E\},

\mathcal{P}(v) = \{u \in V \mid (u, v) \in E\} で定義される.例 6.2 において,\mathcal{S}(v_1) = \{v_2, v_3\},\mathcal{P}(v_1) = \{v_4\} である.

6.5 パス

有向グラフ G = (V, E) において,パス(path)P とは,点の列

P = (v_{i_1}, v_{i_2}, \ldots, v_{i_k})

であって,各 j = 1, 2, \ldots, k-1 に対して (v_{i_j}, v_{i_{j+1}}) \in E を満たし,v_{i_1}, v_{i_2}, \ldots, v_{i_k} がすべて異なるものをいう.パスの長さ(length)は,パスに含まれる辺の数である.例 6.2 において,(v_1, v_2, v_3, v_4) は長さ 3 のパスである.一方,(v_1, v_3, v_4, v_1, v_2) は v_1 を2回含むため,パスではない.

閉路(cycle)とは,点の列 C = (v_{i_1}, v_{i_2}, \ldots, v_{i_k})(k > 1)であって,各 j = 1, 2, \ldots, k-1 に対して (v_{i_j}, v_{i_{j+1}}) \in E を満たし,v_{i_1} = v_{i_k} かつ v_{i_1}, v_{i_2}, \ldots, v_{i_{k-1}} がすべて異なるものをいう.例 6.2 において,(v_1, v_3, v_4, v_1) は閉路である.

6.6 有向非巡回グラフ

有向非巡回グラフ(directed acyclic graph, DAG)とは,閉路を含まない有向グラフである.例 6.2 のグラフは閉路 (v_1, v_3, v_4, v_1) を含むため,DAG ではない.

トポロジカルソート(topological sort)とは,DAG において,すべての辺 (v, u) \in E に対して v が u よりも前に来るように点を並べることである.トポロジカルソートによって得られる点の順序をトポロジカルオーダー(topological order)という.

例 6.3 (トポロジカルソートの例) 図 6.3 は DAG の例である.

コード
# グラフの作成(点を番号で定義)
G = nx.DiGraph()
G.add_edges_from([(1, 2), (1, 3), (2, 4), (3, 4), (3, 5), (4, 5)])

# 点の座標を固定
pos = {1: (0, 0.5), 2: (1.2, 1), 3: (1.2, 0), 4: (2.4, 1), 5: (2.4, 0)}
draw_graph(G, pos)
図 6.3: DAG の例

点を (v_1, v_2, v_3, v_4, v_5) の順に並べる.すべての辺 (v_i, v_j) \in E について i < j であるから,v_i は v_j よりも前に来る.したがって,この並べ方はトポロジカルソートの条件を満たす.

(v_1, v_3, v_2, v_4, v_5) の順に並べても,すべての辺 (v_i, v_j) \in E について v_i が v_j よりも前に来るため,トポロジカルソートの条件を満たす.このように,トポロジカルオーダーは一意に定まるとは限らない.

一方,(v_1, v_2, v_4, v_3, v_5) の順では,辺 (v_3, v_4) について v_4 が v_3 よりも前に来るため,トポロジカルソートの条件を満たさない.

6.7 まとめ

  • グラフは点の集合 V と辺の集合 E から構成され,G = (V, E) と表される.
  • 無向グラフの辺は2つの点からなる集合であり,有向グラフの辺は2つの点の順序対である.
  • 点 v に接続する辺の数を次数と呼ぶ.有向グラフでは,v から出る辺の数を出次数,v に入る辺の数を入次数と呼ぶ.
  • 無向グラフでは,点 v の隣接点の集合を \mathcal{N}(v) と表す.
  • 有向グラフの辺 (v, u) に対して u を v の後続点,v を u の先行点と呼ぶ.点 v の後続点の集合を \mathcal{S}(v),先行点の集合を \mathcal{P}(v) と表す.
  • パスは,各点から次の点へ向かう辺が存在し,すべての点が異なる点の列である.その長さはパスに含まれる辺の数である.
  • 閉路は,ある点から辺をたどり,途中で同じ点を2度通ることなく出発点に戻る点の列である.閉路を含まない有向グラフを有向非巡回グラフ(DAG)と呼ぶ.
  • DAG の点を,すべての辺 (v, u) について v が u よりも前に来るように並べることをトポロジカルソートと呼び,得られる点の順序をトポロジカルオーダーと呼ぶ.トポロジカルオーダーは一意とは限らない.

6.8 練習問題

練習 6.1 図 6.4 の DAG について,次の問いに答えよ.

コード
# グラフの作成(点を番号で定義)
G = nx.DiGraph()
G.add_edges_from([(1, 3), (2, 3), (3, 4), (3, 5), (4, 6), (5, 6)])

# 点の座標を固定
pos = {1: (0, 1), 2: (0, 0), 3: (0.8, 0.5), 4: (1.6, 1), 5: (1.6, 0), 6: (2.4, 0.5)}
draw_graph(G, pos)
図 6.4: 練習問題の DAG
  1. 各点の出次数と入次数を求めよ.
  2. 各点 v について,\mathcal{S}(v) と \mathcal{P}(v) を求めよ.
  3. トポロジカルオーダーをすべて求めよ.