[python] Python에서 그래프 (데이터 구조) 표현

파이썬 에서 그래프 를 어떻게 깔끔하게 표현할 수 있습니까? (처음부터 시작, 즉 라이브러리가 없습니다!) 어떤 데이터 구조 (예 : dicts / tuples / dict (tuples))가 빠르지 만 메모리 효율적일까요? 다양한 그래프 작업 을 수행 할 수 있어야 합니다.
지적했듯이 다양한 그래프 표현 이 도움이 될 수 있습니다. 파이썬으로 구현하는 방법은 무엇입니까? 도서관에 관해서는 이 질문 에 꽤 좋은 답변이 있습니다.



답변

이것은 다소 오래된 질문이지만,이 문제에 걸림돌이되는 사람에게는 실용적인 대답을하겠다고 생각했습니다.

다음과 같은 튜플 목록으로 연결에 대한 입력 데이터를 얻는다고 가정 해 보겠습니다.

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

Python에서 그래프에 가장 유용하고 효율적인 데이터 구조 는 세트사전입니다 . 이것이 우리 Graph클래스 의 기본 구조가 될 것 입니다. 또한 이러한 연결이 호 (방향, 단방향 연결)인지 모서리 (무 방향, 양방향 연결)인지 알아야합니다. 메서드에 directed매개 변수를 추가하여이를 처리합니다 Graph.__init__. 다른 유용한 방법도 추가 할 것입니다.

import pprint
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.items():  # python3: items(); python2: 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)
>>> pretty_print = pprint.PrettyPrinter()
>>> pretty_print.pprint(g._graph)
{'A': {'B'},
 'B': {'D', 'C'},
 'C': {'D'},
 'E': {'F'},
 'F': {'C'}}

>>> g = Graph(connections)  # undirected
>>> pretty_print = pprint.PrettyPrinter()
>>> pretty_print.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')
>>> pretty_print.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')
>>> pretty_print.pprint(g._graph)
{'B': {'D', 'C'},
 'C': {'D', 'F', 'B'},
 'D': {'C', 'E', 'B'},
 'E': {'D', 'F'},
 'F': {'E', 'C'}}

>>> g.add('G', 'B')
>>> pretty_print.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']


답변

NetworkX 는 멋진 Python 그래프 라이브러리입니다. 아직하지 않은 필요한 것을 찾기가 어려울 것입니다.

그리고 그것은 오픈 소스이므로 그들이 알고리즘을 어떻게 구현했는지 볼 수 있습니다. 추가 알고리즘을 추가 할 수도 있습니다.

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


답변

첫째, 고전적인 목록행렬 표현 의 선택은 목적 (표현으로 무엇을하고 싶은지)에 따라 달라집니다. 잘 알려진 문제와 알고리즘은 선택과 관련이 있습니다. 추상 표현 종류의 선택은 구현 방법을 결정합니다.

둘째, 문제는 꼭지점과 모서리가 존재의 관점에서만 표현되어야하는지 아니면 추가 정보를 가지고 있는지 여부입니다.

Python 내장 데이터 유형 관점에서 다른 곳에 포함 된 모든 값은 대상 객체에 대한 (숨겨진) 참조로 표현됩니다. 변수 (즉, 명명 된 참조) 인 경우 이름과 참조는 항상 (내부) 사전에 저장됩니다. 이름이 필요하지 않은 경우 참조를 자체 컨테이너에 저장할 수 있습니다. 여기서는 Python 목록 이 항상 추상화로 목록에 사용됩니다 .

Python 목록은 동적 참조 배열로 구현되고 Python 튜플은 상수 내용이있는 정적 참조 배열로 구현됩니다 (참조 값은 변경할 수 없음). 그 때문에 쉽게 색인화 할 수 있습니다. 이렇게하면 목록을 행렬 구현에도 사용할 수 있습니다.

매트릭스를 표현하는 또 다른 방법은 표준 모듈에 의해 구현 된 배열 array입니다. 저장된 유형, 동종 값에 대해 더 제한적입니다. 요소는 값을 직접 저장합니다. (목록은 대신 값 개체에 대한 참조를 저장합니다). 이렇게하면 메모리 효율성이 향상되고 값에 대한 액세스가 더 빨라집니다.

때로는 bytearray.


답변

두 개의 우수한 그래프 라이브러리
NetworkXigraph가 있습니다. GitHub에서 두 라이브러리 소스 코드를 모두 찾을 수 있습니다. 함수가 어떻게 작성되는지 항상 볼 수 있습니다. 하지만 이해하기 쉽기 때문에 NetworkX를 선호합니다.
그들이 기능을 만드는 방법을 알기 위해 코드를 참조하십시오. 여러 아이디어를 얻은 다음 데이터 구조를 사용하여 그래프를 만드는 방법을 선택할 수 있습니다.


답변