web-dev-qa-db-ja.com

Pythonでのグラフ(データ構造)の表現

Pythongraph をきれいに表現するにはどうすればよいですか? (ゼロから開始、つまりライブラリなし!)
どのデータ構造(たとえばdicts/tuples/dict(tuples))が高速ですが、メモリ効率も良くなりますか?
さまざまなグラフを作成できる必要があります operations .

指摘したように、さまざまな グラフ表現 が役立つ場合があります。 Pythonでそれらを実装するにはどうすればよいですか?

ライブラリに関しては、 この質問 にはかなり良い答えがあります。

88
shad0w_wa1k3r

これはやや古い質問ですが、私はこれにつまずいた人には実用的な答えを与えると思いました。

接続の入力データをタプルのリストとして取得するとしましょう:

[('A', 'B'), ('B', 'C'), ('B', 'D'), ('C', 'D'), ('E', 'F'), ('F', 'C')]

Pythonのグラフに最も有用で効率的であることがわかったデータ構造は、dict of setsです。これは、Graphクラスの基本構造になります。また、これらの接続がアーク(有向、一方向に接続)かエッジ(無向、両方向に接続)かを知る必要があります。 directedパラメーターをGraph.__init__メソッドに追加することで、これを処理します。他の便利なメソッドもいくつか追加します。

from collections import defaultdict


class Graph(object):
    """ Graph data structure, undirected by default. """

    def __init__(self, connections, directed=False):
        self._graph = defaultdict(set)
        self._directed = directed
        self.add_connections(connections)

    def add_connections(self, connections):
        """ Add connections (list of Tuple pairs) to graph """

        for node1, node2 in connections:
            self.add(node1, node2)

    def add(self, node1, node2):
        """ Add connection between node1 and node2 """

        self._graph[node1].add(node2)
        if not self._directed:
            self._graph[node2].add(node1)

    def remove(self, node):
        """ Remove all references to node """

        for n, cxns in self._graph.iteritems():
            try:
                cxns.remove(node)
            except KeyError:
                pass
        try:
            del self._graph[node]
        except KeyError:
            pass

    def is_connected(self, node1, node2):
        """ Is node1 directly connected to node2 """

        return node1 in self._graph and node2 in self._graph[node1]

    def find_path(self, node1, node2, path=[]):
        """ Find any path between node1 and node2 (may not be shortest) """

        path = path + [node1]
        if node1 == node2:
            return path
        if node1 not in self._graph:
            return None
        for node in self._graph[node1]:
            if node not in path:
                new_path = self.find_path(node, node2, path)
                if new_path:
                    return new_path
        return None

    def __str__(self):
        return '{}({})'.format(self.__class__.__name__, dict(self._graph))

find_shortest_pathおよびその他のメソッドを作成するための「読者のための運動」として残しておきます。

これを実際に見てみましょう...

>>> connections = [('A', 'B'), ('B', 'C'), ('B', 'D'),
                   ('C', 'D'), ('E', 'F'), ('F', 'C')]
>>> g = Graph(connections, directed=True)
>>> pprint(g._graph)
{'A': {'B'},
 'B': {'D', 'C'},
 'C': {'D'},
 'E': {'F'},
 'F': {'C'}}

>>> g = Graph(connections)  # undirected
>>> pprint(g._graph)
{'A': {'B'},
 'B': {'D', 'A', 'C'},
 'C': {'D', 'F', 'B'},
 'D': {'C', 'B'},
 'E': {'F'},
 'F': {'E', 'C'}}

>>> g.add('E', 'D')
>>> pprint(g._graph)
{'A': {'B'},
 'B': {'D', 'A', 'C'},
 'C': {'D', 'F', 'B'},
 'D': {'C', 'E', 'B'},
 'E': {'D', 'F'},
 'F': {'E', 'C'}}

>>> g.remove('A')
>>> pprint(g._graph)
{'B': {'D', 'C'},
 'C': {'D', 'F', 'B'},
 'D': {'C', 'E', 'B'},
 'E': {'D', 'F'},
 'F': {'E', 'C'}}

>>> g.add('G', 'B')
>>> pprint(g._graph)
{'B': {'D', 'G', 'C'},
 'C': {'D', 'F', 'B'},
 'D': {'C', 'E', 'B'},
 'E': {'D', 'F'},
 'F': {'E', 'C'},
 'G': {'B'}}

>>> g.find_path('G', 'E')
['G', 'B', 'D', 'C', 'F', 'E']
122
mVChr

NetworkX は素晴らしいPythonグラフライブラリです。あなたが必要とするものを見つけるのは難しいでしょう。

そしてそれはオープンソースなので、彼らがどのようにアルゴリズムを実装したかを見ることができます。追加のアルゴリズムを追加することもできます。

https://github.com/networkx/networkx/tree/master/networkx/algorithms

27
jterrace

最初に、古典的なlist vs. matrix表現の選択は、目的に依存します(表現で何をしたいのか)。よく知られている問題とアルゴリズムは、選択に関連しています。抽象表現の種類の選択により、それをどのように実装するかが決まります。

第二に、問題は頂点とエッジが存在の観点からのみ表現されるべきか、それとも何らかの追加情報を運ぶかどうかです。

Python組み込みデータ型の観点から、他の場所に含まれる値は、ターゲットオブジェクトへの(隠された)参照として表現されます。変数(つまり、名前付き参照)の場合、名前と参照は常に(内部)辞書に格納されます。名前が必要ない場合は、参照を独自のコンテナに保存できます-ここではおそらくPythonリストリストに抽象化として常に使用されます。

Pythonリストは、参照の動的配列として実装されます。Pythonタプルは、コンテンツが一定の参照の静的配列として実装されます(参照の値は変更できません)。そのため、簡単にインデックスを作成できます。このように、リストは行列の実装にも使用できます。

マトリックスを表す別の方法は、標準モジュールarrayによって実装される配列です。格納された型、同種の値に関してより制約されます。要素は値を直接保存します。 (代わりに、値オブジェクトへの参照がリストに保存されます)。これにより、メモリ効率が向上し、値へのアクセスも高速になります。

場合によっては、bytearrayのようなさらに制限された有用な表現が見つかることがあります。

7
pepr

2つの優れたグラフライブラリ NetworkXigraph があります。 GitHubで両方のライブラリソースコードを見つけることができます。関数がどのように書かれているかはいつでも確認できます。しかし、私はNetworkXの方がわかりやすいので気に入っています。
コードを参照して、どのように関数を作成するかを確認してください。複数のアイデアを取得し、データ構造を使用してグラフを作成する方法を選択できます。

6
Vineet Jain