- 그래프(Graph)
- 깊이 우선 탐색(Depth-First Search)
- 너비 우선 탐색(Breadth-First Search)
Chapter 3. Graph-theoretic Models 강의를 듣고 정리한 내용입니다.
그래프(Graph)
- 그래프는 꼭짓점 혹은 노드로 이루어지고, 이들은 간선 혹은 변으로 이루어짐
- 노드의 집합(꼭짓점)
- 각 노드는 다른 노드와 관계된 정보를 담고 있음
- ex) 학생의 성적, 그래프는 한 반의 성적을 모아놓은 것이라고 할 수 있음
- 간선, 변이
- 노드의 쌍을 연결
- 간선을 이용해 그래프를 만드는 2가지 방법
- 무방향
- 유향(방향 그래프의 일종이 트리)
- 사용 예시
- 개체 사이의 관계 표시
- 예) 파리에서 런던으로 여행을 갈 때 노드는 각 도시, 연결은 도시사이 레일
- 약의 발견
- 각각의 관계를 파악하면서 복잡한 분자를 모델링
- 개체 사이의 관계 표시
- 유용한 이유
- 세상에 존재하는 여러관계는 그래프로 표현하기 적합
- 컴퓨터 네트워크, 교통 네트워크, 금융 네트워크, 정치네트워크, 범죄 네트워크, 사회 네트워크 등
- 관심이 있는것의 관계를 찾을 수 있음
- 그래프를 만드는 방법
- 두 요소 사이에 연속된 간선이 있는지 확인
- 최소비용 혹은 최단 경로를 찾을 수 있는지 확인
- 그래프 분할 문제로 접근(그래프에서 연결된 요소만 분리)
-- python 객체 클래스 상속받음
class Node(object):
def __init__(self, name):
self.name = name
def getName(self):
return self.name
def __str__(self):
return self.name
class Edge(object):
def __init__(self, src, dest):
self.src = src
self.dest = dest
def getSource(self):
return self.src
def getDestination(self):
return self.dest
def __str__(self):
return self.src.getName() + '->'\
+ self.dest.getName()
- 중요한 가정: 인자로 받는 시작점과 도착점은 노드여야한다(실제 인스턴스)
- 간선안에서 노드를 가르키는 포인트를 얻고 노드에서 해당하는 함수에 접근하고 출력
인접 행렬 : 행은 시작점, 열은 도착점
- 유향 그래프에선 행렬은 대칭행렬이 아님
인접 리스트 : 노드별로 목적지 리스트로 만들어줌
class Digraph(object):
def __init__(self):
self.edges = {} #노드 키를 딕셔너라에 저장
def addNode(self.node): # 노드를 추가할때 딕셔너리가 있는지 확인
if node in self.edges:
raise ValueError('Duplicate node')
else:
self.edges[node] = [] #키가 노드 자체
def addEdge(self, edge):
src = edge.getSource()
dest = edge.getDestination()
if not (src is self.edges and dest in self.edges):
raise ValueError('node is not grape')
self.edges[src].append(dest)
def childrenOf(self, node):
return self.edges[node]
def hasNode(self, node):
return node in self.edges
def getNode(sel, name):
for n in self.edges:
if n.getName() == name:
return n
raise NameError(name)
def __str__(self):
result = ''
for src in self.edges:
for dest in self.edges[src]:
result = result + src.getName() + '->'\
+ dest.getName() + '\n'
return result[:-1]
- 노드는 딕셔너리의 키, 간선은 키와 목적노드 리스트로 표현된다.
- 특정 시작점과 도착점 사이 간선이 있는지 알고 싶을 때 딕셔너리에서 도착점이 리스트에 있는지 확인하면 된다.
- 특정노드의 자식이 알고싶으면 딕셔너리에 가서 해당 노드와 연관된 리스트를 보면 된다.
class Graph(Digraph):
def addEdge(self, edge):
Digraph.addEdge(self, edge)
rev = Edge(edge.getDsetination(), edge.getSource())
Digraph.addEdge(self, rev)
- 시작점에서 도착점으로 가는 간선 추가, 다른 방향의 간선도 만들어준다.
- 이점) 간선의 방향성이 없어 양방향으로 갈 수 있다.
- 명시적으로 호출하여 서브클래스로부터 상속받는다.
그래프 탐색
- 한 노드에서 다른 노드로 가는 최단 경로를 구하는 방법은?
- 최단경로 : 시작점의 첫 간선 ~ 도착점의 마지막 간선
- 각 간선에는 가중치가 있음 → 단순히 거리를 가중치로 둘 수 있음
- 일종의 사슬처럼 보임
- 알고싶은 것 : 최소 단계 수(시작점-도착점 사이의 간선들)
1) 깊이 우선 탐색(Depth-First Search)
- 루트 노드에서 시작해 다음 분기로 넘어가기 전에 해당 분기를 모두 탐색하는 방법
- 막힐때까지 다음 간선을 따라 감(막히면 돌아옴)
- 쉽게 수정 가능
- 루프의 가능성이 있기 떄문에 경로를 기억해야함
- 이미 들렀던 노드는 가지않음
- 첫 간선을 따라가다 올바르게 왔는지 확인, 올바르지않으면 루프
- 목표 노드에 도착하거나 선택지가 없어질 때까지 반복
- 특징
- 자기 자신을 호출하는 순환 알고리즘
- 어떤 노드를 방문했는지 반드시 확인
def DFS(graph, start, end, path, shortest, toPrint=False):
path = path[start]
if toPrint:
print('Current DFS path', printPath(path))
return path
for node in graph.childrenOf(start):
if node not in path:
if shortest == None or len(path) < len(shortest):
newPath = DFS(graph, node, end, path, shortest, toPrint)
if newPath != None:
shorest = newPath
elif toPrint:
print('Already visited', node)
return shortest
def shortestPath(graph, start, end, toPrint = False):
return DFS(graph, start, end, [], None, toPrint)
2) 너비 우선 탐색(Breadth-First Search)
- 루트 노드에서 시작해 인접한 노드를 먼저 탐색하는 방법
- 길이가 같은 경로를 탐색
- 큐를 계속 돌면서 남은 경로를 탐색
- 쉽게 수정 불가
- 최소 가중 경로가 최소 개수 경로가 아닐 수 있어서 고려해서 수정해야함
- 특정한 순서로 시작점에서 나가는 모든 변을 고려
- 도착점을 찾거나, 선택지가 없어질 때까지 반복
- 변의 선택지가 없어지면, 시작점에서의 거리가 같은 다음 꼭짓점으로 가서 과정을 반복
- 꼭짓점의 선택지가 없어지면, 그래프의 다음 레벨로 가서 반복
- 특징
- 루트 노드에서 목표노드까지의 최단 거리를 보장
def BFS(graph, start, end, toPrint=False):
initPath = [start]
pathQueue = [initpath]
while len(pathQueue) != 0:
pathQueue.pop(0)
if toPrint:
print('Current BFS path', printPath(tmpPath))
lastNode = tmpPath[-1]
if lastNode == end:
return tmpPath
for nextNode in graph.childrenOf(lastNode):
if nextNode not in tmpPath:
newPath = tmpPath + [nextNode]
pathQueue.append(newPath)
return None
- n 루프 이상의 경로를 탐색하기 전에 n 루프의 모든 경로를 탐색
다음강의 : Chapter 4. Stochastic Thinking