Python中的双向搜索

400 阅读4分钟

读者朋友们好,在这篇文章中,让我们试着了解什么是双向搜索,它的优点、缺点,以及它在python中的实现。

什么是双向搜索?

一种叫做双向搜索的图搜索算法同时进行两次搜索。当这两个搜索在中途相遇时,一个停止从起点向前移动,另一个停止从目的地向后移动。对于具有单一起始状态和单一目标状态的问题,它是有帮助的。

在实现 k=1,2,...的双向搜索时,可以使用深度优先迭代深化搜索(DFID)。在第k次迭代中,不是存储状态,而是简单地与从前进方向产生的存储状态相匹配,前进方向的所有状态都是用广度优先搜索从开始状态到深度k,从目标状态到深度k和深度k+1产生的。

在这里,为了识别奇数长度的答案,需要进行反向或后向搜索到深度k+1。如果确定了一个匹配,那么就可以确定从开始到匹配状态以及从匹配状态到目标状态的路线。应该注意的是,每个节点都有一个与其后继者以及其父辈的链接。这些链接将有助于生成一条从起点到目标状态的完整路径。

双向搜索是如何工作的?

让我们用一个现有的图来说明这个方法的工作。考虑以下图,如图所示。考虑在图中找到一条从第一个节点1到最后一个元素16的路线。

迭代

在Python中实现双向搜索

class adjacent_Node:
	
	def __init__(self, v):
		
		self.vertex = v
		self.next = None


class bidirectional_Search:
	
	def __init__(self, vertices):
		
		self.vertices = vertices
		self.graph = [None] * self.vertices
		
		self.source_queue = list()
		self.last_node_queue = list()
		
		
		self.source_visited = [False] * self.vertices
		self.last_node_visited = [False] * self.vertices
		
		
		self.source_parent = [None] * self.vertices
		self.last_node_parent = [None] * self.vertices
		
	
	def AddEdge(self, source, last_node):
		
		node = adjacent_Node(last_node)
		node.next = self.graph[source]
		self.graph[source] = node

		node = adjacent_Node(source)
		node.next = self.graph[last_node]
		self.graph[last_node] = node
		
	
	def breadth_fs(self, direction = 'forward'):
		
		if direction == 'forward':
		
			current = self.source_queue.pop(0)
			connected_node = self.graph[current]
			
			while connected_node:
				vertex = connected_node.vertex
				
				if not self.source_visited[vertex]:
					self.source_queue.append(vertex)
					self.source_visited[vertex] = True
					self.source_parent[vertex] = current
					
				connected_node = connected_node.next
		else:
			
			current = self.last_node_queue.pop(0)
			connected_node = self.graph[current]
			
			while connected_node:
				vertex = connected_node.vertex
				
				if not self.last_node_visited[vertex]:
					self.last_node_queue.append(vertex)
					self.last_node_visited[vertex] = True
					self.last_node_parent[vertex] = current
					
				connected_node = connected_node.next
				
	
	def is_intersecting(self):
		
		#
		for i in range(self.vertices):
			if (self.source_visited[i] and
				self.last_node_visited[i]):
				return i
				
		return -1

	
	def path_st(self, intersecting_node,
				source, last_node):
						
	
		path = list()
		path.append(intersecting_node)
		i = intersecting_node
		
		while i != source:
			path.append(self.source_parent[i])
			i = self.source_parent[i]
			
		path = path[::-1]
		i = intersecting_node
		
		while i != last_node:
			path.append(self.last_node_parent[i])
			i = self.last_node_parent[i]
			
		path = list(map(str, path))
		
		print(' '.join(path))
	
	def bidirectional_search(self, source, last_node):
		
		self.source_queue.append(source)
		self.source_visited[source] = True
		self.source_parent[source] = -1
		
		self.last_node_queue.append(last_node)
		self.last_node_visited[last_node] = True
		self.last_node_parent[last_node] = -1

		while self.source_queue and self.last_node_queue:
			
		
			self.breadth_fs(direction = 'forward')
			
			self.breadth_fs(direction = 'backward')
				
			intersecting_node = self.is_intersecting()
			
			if intersecting_node != -1:
				print("Path exists between {} and {}".format(source, last_node))
				print("Intersection at : {}".format(intersecting_node))
				self.path_st(intersecting_node,
								source, last_node)
				exit(0)
		return -1


if __name__ == '__main__':
	
	n = 17
	
	source = 1
	
	last_node = 16
	
	my_Graph = bidirectional_Search(n)
	my_Graph.AddEdge(1, 2)
	my_Graph.AddEdge(1, 3)
	my_Graph.AddEdge(1, 4)
	my_Graph.AddEdge(2, 5)
	my_Graph.AddEdge(2, 6)
	my_Graph.AddEdge(3, 7)
	my_Graph.AddEdge(4, 8)
	my_Graph.AddEdge(4, 9)
	my_Graph.AddEdge(5, 10)
	my_Graph.AddEdge(6, 10)
	my_Graph.AddEdge(10, 11)
	my_Graph.AddEdge(7, 11)
	my_Graph.AddEdge(7, 12)
	my_Graph.AddEdge(8, 13)
	my_Graph.AddEdge(9, 13)
	my_Graph.AddEdge(10, 6)
	my_Graph.AddEdge(11, 14)
	my_Graph.AddEdge(12, 15)
	my_Graph.AddEdge(13, 15)
	my_Graph.AddEdge(14, 16)
	my_Graph.AddEdge(15, 16)
	
	out = my_Graph.bidirectional_search(source, last_node)
	
	if out == -1:
		print("No path between {} and {}".format(source, last_node))

OUTPUT。

路径存在于1和16之间

交叉点在。8

1 4 8 13 15 16

双向搜索的复杂度

采用这种方法的原因是,两个搜索中的每一个的时间复杂度都是O(b^d/2),O(b^d/2+b^d/2)比从开始到目标的一个搜索的运行时间要小得多,后者是O(b^d)。这种搜索可以在一个已经存在的图/树中进行,也可以生成一个搜索图/树作为搜索的一部分。

优势

  • 我们收到所需结果的速度是双向搜索的关键优势之一。
  • 通过同时进行多个搜索,搜索时间被大大缩短。
  • 用户还可以节约资源,因为存储所有的搜索所需的内存较少。

缺点

  • 如果算法不够强大,不能识别出搜索应该终止的交点,就有可能出现无限循环。
  • 另一个挑战是这种算法的实现需要额外的代码和指令,为了进行这样的搜索,每个节点和步骤都应该被仔细实现。
  • 双向搜索有一个基本问题,即用户必须知道目标状态才能使用它,从而减少了它的使用案例。

总结

它确实有一些缺点,双向搜索是最受欢迎和被广泛研究的搜索算法之一,因为当目标状态在搜索开始前就已经知道时,它是最有效和最快速的方法来到达所需的搜索结果。