Eric Grimson, John Guttag, and Ana Bell. 6.0002 Introduction to Computational Thinking and Data Science. Fall 2016. Massachusetts Institute of Technology: MIT OpenCourseWare, https://ocw.mit.edu. License: Creative Commons BY-NC-SA.
Lecture 3. Graph-theoretic Models
ある現実に存在するものや,抽象的な概念の集合(nodes)があって,それらの関係性(edges)を記述する方法にGraphがある.あるnodeから出るedgeを辿って他のnodeにたどり着けるとき,渡ってきたedgeたちをからへのpathという.edgeはnode間の関係性を表すが,必ずしも両方向でない関係性(好き嫌い,因果関係,所属)のような関係を記述するgraphをdirected graph(有向グラフ)といい,必ず両方向である関係(血縁,路線図)を記述するgraphをundirected graph(無向グラフ)という.undirected graphはdirected graphの特別な場合である.また,edge間に重みがつけられているgraphもある。.
特に重要なグラフの一種にtreeがある.treeでは任意の2つのedgeが一意のpathで結ばれている.
例 
 
The network graph formed by Wikipedia editors (edges) contributing to different Wikipedia language versions (vertices) during one month in summer 2013
早速graphを実装する.
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):
    """ Assumes src and dest are nodes"""
    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.geName()
class Digraph(object):         # directed graphのクラス
  """
  edge is a dict mapping each node
  to a list of its children.
  """
  def __init__(self):
    self.edges = {}
  def addNone(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 in self.edges and dest in self.edges):
      raise ValueError('Node not in graph')
    self.edges[src].append(dest)
  def childrenOf(self, node):
    return self.edges[node]
  def hasNode(self, node):
    return node in self.edges
  def getNode(self, 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] # omit final newline
  class Graph(Digraph):
    """
    transform a directed graph to a undirected graph
    """
    def addEdge(self, edge):
      Digraph.addEdge(self, edge)
      rev = Edge(edge.getDestination(), edge.getSource())
      Digraph.addEdge(self, rev)
古典的なグラフ最適化問題に,最短経路問題がある.node1, node2を結ぶ最短のpathを見つける問題である.ループ構造がある可能性があるので,無限ループを避ける工夫が必要.depth-first search(深さ優先探索)とbreadth-first search(幅優先探索)がある.
Depth First search
初期nodeから辿れるpathを目的nodeに到達するか,到達しないと判明するまで(ループ構造は,辿ってきたpathを保存しておいて照合することで回避する)辿る.
def DFS(graph, start, end, path, shortest, toPrint=False):
  path = path + [start]
  if toPrint:
    print('Current DFS path:', printPath(path))
  if start == end:
    return path
  for node in graph.childrenOf(start):
    if node not in path: #avoid cycles
      if shortest == None or len(path) < len(shortest):
        newPath = DFS(graph, node, end, path, shortest, toPrint)
        if newPath != Node:
          shortest = newPath
        elif toPrint:
          print('Already visited', node)
  return shortest
def  shortestPath(graph, start, end, toPrint=False):
  """ a wrapper function of DFS"""
  return DFS(graph, start, end, [], None, toPrint)
Breadth-first Search(BFS)
初期nodeから長さnのpathのnodeに目的nodeがあるかすべて探索してから,長さn+1のpathのnodeたちを調べる.depth-first searchよりもふつう低速である.
def BFS(graph, start, end, toPrint=False):
  initPath =  [start]
  pathQueue = [initPath]
  while len(PathQueue) != 0:
    tmpPath = 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
0 件のコメント:
コメントを投稿