凯文培根游戏的最短路径图遍历

时间:2017-12-01 03:57:43

标签: python algorithm graph-theory shortest-path breadth-first-search

我一直在尝试为热门的凯文培根游戏创建图表。我已经创建了图形和顶点类,但是在创建宽度第一搜索方法来遍历图形并找到从凯文培根到演员的最短路径时遇到了一些麻烦,并在路上打印出边缘。 用户应该输入一个演员,程序应该找到从凯文培根到该演员的最短路径。然后用户将继续输入演员,并且将获取该演员的最短路径,并打印出凯文培根号码,否则它将打印出来。

有一个顶点和图表类。顶点类是一个字典,它包含它连接的其他顶点和边缘。

我正在使用的数据如下所示:

顶点:

[" Kevin Bacon"," actor1"," actor2"," actor3"," actor4", "演员5","演员6"]

边缘:

(" Kevin Bacon"," actor1"," movie1")

(" Kevin Bacon","演员2"," movie1")

(" actor1"," actor2"," movie1")

("演员1","演员3","电影2")

("演员3","演员2","电影3")

("演员3","演员4","电影4")

("演员5","演员6","电影5")

电影是边缘名称或权重,而元组的其他部分是顶点。我希望BFS算法打印出所有边缘和凯文培根数字,或者打印出如果无法联系到演员则不可能。

到目前为止,这是代码。任何建议和帮助表示赞赏。

感谢您的时间

class Vertex:
    '''
    keep track of the vertices to which it is connected, and the weight of each edge
    '''
    def __init__(self, key):
        '''

        '''
        self.ID = key
        self.connected_to = {}

    def add_neighbor(self, neighbor, weight=0):
        '''
        add a connection from this vertex to anothe
        '''
        self.connected_to[neighbor] = weight

    def __str__(self):
        '''
        returns all of the vertices in the adjacency list, as represented by the connectedTo instance variable
        '''
        return str(self.ID) + ' connected to: ' + str([x.ID for x in self.connected_to])

    def get_connections(self):
        '''
        returns all of the connections for each of the keys
        '''
        return self.connected_to.keys()

    def get_ID(self):
        '''
        returns the current key id
        '''
        return self.ID

    def get_weight(self, neighbor):
        '''
        returns the weight of the edge from this vertex to the vertex passed as a parameter
        '''
        return self.connected_to[neighbor]


class Graph:
    '''
    contains a dictionary that maps vertex names to vertex objects. 
    '''
    def __init__(self):
        '''

        '''
        self.vert_list = {}
        self.num_vertices = 0

    def __str__(self):
        '''

        '''
        edges = ""
        for vert in self.vert_list.values():
            for vert2 in vert.get_connections():
                edges += "(%s, %s)\n" %(vert.get_ID(), vert2.get_ID())
        return edges

    def add_vertex(self, key):
        '''
        adding vertices to a graph 
        '''
        self.num_vertices = self.num_vertices + 1
        new_vertex = Vertex(key)
        self.vert_list[key] = new_vertex
        return new_vertex

    def get_vertex(self, n):
        '''

        '''
        if n in self.vert_list:
            return self.vert_list[n]
        else:
            return None

    def __contains__(self, n):
        '''
        in operator
        '''
        return n in self.vert_list

    def add_edge(self, f, t, cost=0):
        '''
        connecting one vertex to another
        '''
        if f not in self.vert_list:
            nv = self.add_vertex(f)
        if t not in self.vert_list:
            nv = self.add_vertex(t)
        self.vert_list[f].add_neighbor(self.vert_list[t], cost)

    def get_vertices(self):
        '''
        returns the names of all of the vertices in the graph
        '''
        return self.vert_list.keys()

    def __iter__(self):
        '''
        for functionality
        '''
        return iter(self.vert_list.values())

    def bfs(self):
        '''
        Needs to be implemented
        '''
        pass

1 个答案:

答案 0 :(得分:1)

  1. 找演员
  2. 检查演员是否是Kevin Bacon
  3. 如果演员是凯文·培根,那就沿着你走的路走吧
  4. 如果演员不是凯文·培根,那么找到所有与这位演员相关的演员,你们还没有检查过。
  5. 将此演员所连接的所有演员添加到您的列表中以进行检查。
  6. 这里最难的问题是记录您已经访问过的顶点。因此,我认为您的算法应该检查顶点列表。一些假设:

    • 每个顶点只列出一次。
    • 顶点仅为单向。这意味着如果你想从Actor 1转到Actor 2,从Actor 2转到Actor 1,你需要两个顶点,每个顶点对应一个。
    • 你有权重,但我不知道它们与此有何关联。我会尝试实现它们。此外,您的默认权重不应为0,否则所有路径将同样短(0 * n = 0)。

    好的,我们走了。

    def bfs(self, actor):
        from heapq import heappush, heappop
        if actor == "Kevin Bacon":
            return print("This actor is Kevin Bacon!")
        visited = set()
        checked = []
        n = 0
        heappush(checked, (0, n, [self.get_vertex(actor)]))
        # if the list is empty we haven't been able to find any path
        while checked:
            # note that we pop our current list out of the list of checked lists,
            # if all of the children of this list have been visited it won't be
            # added again
            current_list = heappop(checked)[2]
            current_vertex = current_list[-1]
            if current_vertex.ID == "Kevin Bacon":
                return print(current_list)
            for child in current_vertex.get_connections():
                if child in visited:
                    # we've already found a shorter path to this actor
                    # don't add this path into the list
                    continue
                n += 1
                # make a hash function for the vertexes, probably just
                # the hash of the ID is enough, ptherwise the memory address
                # is used and identical vertices can be visited multiple times
                visited.add(child)
                w = sum(current_list[i].get_weight(current_list[i+1])
                        for i in range(len(current_list)-1))
                heappush(checked, (w, n, current_list + [child]))
        print("no path found!")
    

    您还应该为顶点类实现__repr __()方法。使用我使用的那个,输出看起来像这样:

    g = Graph()
    for t in [("Kevin Bacon", "actor1", "movie1")
    ,("Kevin Bacon", "actor2", "movie1")
    ,("actor1", "actor2", "movie1")
    ,("actor1", "actor3", "movie2")
    ,("actor3", "actor2", "movie3")
    ,("actor3", "actor4", "movie4")
    ,("actor5", "actor6", "movie5")]:
        g.add_edge(t[0],t[1],cost=1)
        g.add_edge(t[1],t[0],cost=1)
    
    g.bfs("actor4")
    # prints [Vertex(actor4), Vertex(actor3), Vertex(actor2), Vertex(Kevin Bacon)]
    

    我最初不会使用heapq来做这件事,但最终决定我也可以。基本上,您需要对已检查的列表进行排序,以便首先获得最短路径。最简单的方法是在每次要从顶部弹出最小值时对列表进行排序,但是当列表变大时,这可能会非常慢。 Heapq可以以更有效的方式对列表进行排序,但是没有关键方法来获取我们添加的列表的最小值,因此我们需要使用元组来伪造它。元组中的第一个值是路径的实际成本,而第二个值只是一个“断路器”,因此我们不会尝试比较Vertexes(它们不是有序的,如果我们尝试这样做会引发异常) )。