파이썬 에서 그래프 를 어떻게 깔끔하게 표현할 수 있습니까? (처음부터 시작, 즉 라이브러리가 없습니다!) 어떤 데이터 구조 (예 : dicts / tuples / dict (tuples))가 빠르지 만 메모리 효율적일까요? 다양한 그래프 작업 을 수행 할 수 있어야 합니다.
지적했듯이 다양한 그래프 표현 이 도움이 될 수 있습니다. 파이썬으로 구현하는 방법은 무엇입니까? 도서관에 관해서는 이 질문 에 꽤 좋은 답변이 있습니다.
파이썬 에서 그래프 를 어떻게 깔끔하게 표현할 수 있습니까? (처음부터 시작, 즉 라이브러리가 없습니다!) 어떤 데이터 구조 (예 : 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']
heapq
lib를 사용하여 세트 대신 튜플 목록을 힙 화하는 것입니다. 예를 들어 그래프는 같은 힙의 DICT 될 것이다 : _graph = {'A': heapify([(0.3, 'D'), (0.5, 'B'), (0.75, 'A'), (0.9, 'C')])}
(참고 : 당신은 실제로 사용하지 것이다 heapify
lib 디렉토리에 대한 도움말을 읽고,이 같은) 당신이 사용할 수있는, heapq
가중 가장자리를 삽입하고 얻을 기능.
log
시간 액세스를 의미합니다 . 그러나 nodeID와 weight를 매핑하는 데 사용한 사전을 확장하는 방법은 무엇입니까?
NetworkX 는 멋진 Python 그래프 라이브러리입니다. 아직하지 않은 필요한 것을 찾기가 어려울 것입니다.
그리고 그것은 오픈 소스이므로 그들이 알고리즘을 어떻게 구현했는지 볼 수 있습니다. 추가 알고리즘을 추가 할 수도 있습니다.
https://github.com/networkx/networkx/tree/master/networkx/algorithms
graph.py --> class Graph
. 그리고 내가보고 싶은 것은 그들이 사용하는 방법 __iter__
입니다.
첫째, 고전적인 목록 과 행렬 표현 의 선택은 목적 (표현으로 무엇을하고 싶은지)에 따라 달라집니다. 잘 알려진 문제와 알고리즘은 선택과 관련이 있습니다. 추상 표현 종류의 선택은 구현 방법을 결정합니다.
둘째, 문제는 꼭지점과 모서리가 존재의 관점에서만 표현되어야하는지 아니면 추가 정보를 가지고 있는지 여부입니다.
Python 내장 데이터 유형 관점에서 다른 곳에 포함 된 모든 값은 대상 객체에 대한 (숨겨진) 참조로 표현됩니다. 변수 (즉, 명명 된 참조) 인 경우 이름과 참조는 항상 (내부) 사전에 저장됩니다. 이름이 필요하지 않은 경우 참조를 자체 컨테이너에 저장할 수 있습니다. 여기서는 Python 목록 이 항상 추상화로 목록에 사용됩니다 .
Python 목록은 동적 참조 배열로 구현되고 Python 튜플은 상수 내용이있는 정적 참조 배열로 구현됩니다 (참조 값은 변경할 수 없음). 그 때문에 쉽게 색인화 할 수 있습니다. 이렇게하면 목록을 행렬 구현에도 사용할 수 있습니다.
매트릭스를 표현하는 또 다른 방법은 표준 모듈에 의해 구현 된 배열 array
입니다. 저장된 유형, 동종 값에 대해 더 제한적입니다. 요소는 값을 직접 저장합니다. (목록은 대신 값 개체에 대한 참조를 저장합니다). 이렇게하면 메모리 효율성이 향상되고 값에 대한 액세스가 더 빨라집니다.
때로는 bytearray
.