トポロジカルソート

本稿では NetworkX の提供する機能を利用して、有向グラフのトポロジカルソートを実行する方法について述べる。 NetworkX でトポロジカルソートを実現する関数は nx.topological_sortnx.lexicographical_topological_sort である。まずは前者を見ていく。

先にグラフ概形とサンプルコード全体を示す。

Public Domain
#!/usr/bin/env python
"""tsort.py: demonstrate NetworkX (topological_sort)
"""
import networkx as nx

def main():
    """The main function.

    Returns:
      None
    """

    G = setup_graph()
    print_sort(G)

def setup_graph():
    """Create a DAG to demonstrate topological_sort.

    Returns:
      (DiGraph): A DAG.
    """
    G = nx.DiGraph()

    # http://en.wikipedia.org/wiki/File:Directed_acyclic_graph.png
    G.add_edges_from((
        (7, 11),
        (5, 11),
        (11, 2),
        (11, 9),
        (3, 8),
        (8, 9),
        (3, 10),
        (7, 8),
        (11, 10)))

    #assert nx.is_directed_acyclic_graph(G)
    return G

def print_sort(G):
    """Execute topological_sort and print the result.

    Args:
      G (Graph): A DAG.

    Returns:
      None
    """
    print(list(nx.topological_sort(G)))

if __name__ == '__main__':
    main()

基本事項

グラフがトポロジカルソートを適用できる条件は、グラフが有向かつ閉路がないことが条件となる。つまり、グラフが DAG であることが条件である。 NetworkX では関数 nx.is_directed_acyclic_graph でグラフが DAG であるか否かをテストできる。そして実は DAG 性の判定はトポロジカルソートで実装されている。

そして、トポロジカルソートで少々注意を要するのは、一般的にはその結果が一意に決まらないということだろうか。サンプルコードは Wikipedia で DAG を論じるときによく参照されるグラフを拝借した。ページ中程に複数のソート結果が紹介されている。

関数 print_sort ではジェネレーター nx.topological_sort を呼び出し、実行結果を標準出力に書き出す。結果はこのようになった。見事に Wikipedia のどのソート結果とも異なる順列が得られた。

[7, 5, 11, 3, 8, 9, 10, 2]

しかし、これもまたトポロジカルソートの要件を満たしていることが容易に確認できる。ちなみに図のグラフのノードにわざわざ私が色を塗ったのは、色の明るいノードの順に得られるのでは、という予想だった。そしてそれは見事に外れたようだ。

辞書式トポロジカルソート

NetworkX におけるもう一つのトポロジカルソートの実装である。 DFS 法を再帰的に呼び出す方式によるものだ。

試しに先のグラフに lexicographical 版を適用してみると、異なるソート結果が得られた。 Wikipedia の <smallest-numbered available vertex first> の結果と一致する。

>>> list(nx.lexicographical_topological_sort(G))
[3, 5, 7, 8, 11, 2, 9, 10]

ちなみに先に紹介した nx.topological_sort の実装はスキーナ本(邦訳:『アルゴリズム実装マニュアル』)から取ってきているらしい。