双向搜索

3
我正在尝试在Python中实现双向搜索。
据我所知,我应该以某种方式合并两个广度优先搜索,一个从起始(或根)节点开始,另一个从目标(或结束)节点开始。当两个广度优先搜索在同一顶点“相遇”时,双向搜索终止。我已经实现了BFS,代码如下:
def bfs(graph, start):
    path = []
    queue = [start]
    while queue:
        vertex = queue.pop(0)
        if vertex not in path:
            path.append(vertex)
            queue.extend(graph[vertex])
    return path

你能给我提供一个双向图搜索的代码示例(使用Python),或者是包含代码的链接吗?


你可以在这里找到一个很好的例子:https://stackoverflow.com/questions/32624035/implementing-bidirectional-a-shortest-path-algorithm - MEdwin
我不理解搜索终止的概念,请指导我。 - AAT
2个回答

7
这似乎是一个有趣的问题,所以我试着解决它。这是我的尝试。你没有说你的图形格式是什么,所以我猜测它是像下面这样的字典格式:
example_graph = {0:[1,2], 1:[0,3,4], 3:[1], 4:[1], 2:[0,5,6], 5:[2], 6:[2]}

代码运行通过当前活动顶点的列表,从指定的起始和目标顶点开始,并通过{顶点:列表}的字典记住了如何通过它们。如果一个活动顶点发现自己紧邻另一个从另一端开始的活动顶点,它将合并列表并返回结果,否则它将扩展所有路径并继续运行。
def bi_directional_search(graph, start, goal):
    # Check if start and goal are equal.
    if start == goal:
        return [start]
    # Get dictionary of currently active vertices with their corresponding paths.
    active_vertices_path_dict = {start: [start], goal: [goal]}
    # Vertices we have already examined.
    inactive_vertices = set()

    while len(active_vertices_path_dict) > 0:

        # Make a copy of active vertices so we can modify the original dictionary as we go.
        active_vertices = list(active_vertices_path_dict.keys())
        for vertex in active_vertices:
            # Get the path to where we are.
            current_path = active_vertices_path_dict[vertex]
            # Record whether we started at start or goal.
            origin = current_path[0]
            # Check for new neighbours.
            current_neighbours = set(graph[vertex]) - inactive_vertices
            # Check if our neighbours hit an active vertex
            if len(current_neighbours.intersection(active_vertices)) > 0:
                for meeting_vertex in current_neighbours.intersection(active_vertices):
                    # Check the two paths didn't start at same place. If not, then we've got a path from start to goal.
                    if origin != active_vertices_path_dict[meeting_vertex][0]:
                        # Reverse one of the paths.
                        active_vertices_path_dict[meeting_vertex].reverse()
                        # return the combined results
                        return active_vertices_path_dict[vertex] + active_vertices_path_dict[meeting_vertex]

            # No hits, so check for new neighbours to extend our paths.
            if len(set(current_neighbours) - inactive_vertices - set(active_vertices))  == 0:
                # If none, then remove the current path and record the endpoint as inactive.
                active_vertices_path_dict.pop(vertex, None)
                inactive_vertices.add(vertex)
            else:
                # Otherwise extend the paths, remove the previous one and update the inactive vertices.
                for neighbour_vertex in current_neighbours - inactive_vertices - set(active_vertices):
                    active_vertices_path_dict[neighbour_vertex] = current_path + [neighbour_vertex]
                    active_vertices.append(neighbour_vertex)
                active_vertices_path_dict.pop(vertex, None)
                inactive_vertices.add(vertex)

    return None

0
import queue

class Node:
  def __init__(self, val):
    self.val = val
    self.neighbors = None
    self.visited_right = False  # whether the node was reached by the BFS that started from source
    self.visited_left = False  # whether the node was reached by the BFS that started from destination
    self.parent_right = None  # used for retrieving the final path from start to the meeting point
    self.parent_left = None  # used for retrieving the final path from the meeting point to destination


def bidirectional_search(s, t):

  def extract_path(node):
    """return the path when both BFS's have met"""
    node_copy = node
    path = []

    while node:
      path.append(node.val)
      node = node.parent_right

    path.reverse()
    del path[-1]  # because the meeting node appears twice

    while node_copy:
      path.append(node_copy.val)
      node_copy = node_copy.parent_left
    return path


  q = queue.Queue()
  q.put(s)
  q.put(t)
  s.visited_right = True
  t.visited_left = True

  while not q.empty():
    n = q.get()

    if n.visited_left and n.visited_right:  # if the node visited by both BFS's
      return extract_path(n)

    for node in n.neighbors:
      if n.visited_left == True and not node.visited_left:
        node.parent_left = n
        node.visited_left = True
        q.put(node)
      if n.visited_right == True and not node.visited_right: 
        node.parent_right = n
        node.visited_right = True
        q.put(node)

  return False

n0 = Node(0)
n1 = Node(1)
n2 = Node(2)
n3 = Node(3)
n4 = Node(4)
n5 = Node(5)
n6 = Node(6)
n7 = Node(7)
n0.neighbors = [n1, n5]
n1.neighbors = [n0, n2, n6]
n2.neighbors = [n1]
n3.neighbors = [n4, n6]
n4.neighbors = [n3]
n5.neighbors = [n0, n6]
n6.neighbors = [n1, n3, n5, n7]
n7.neighbors = [n6]
print(bidirectional_search(n0, n4))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接