Python 带BFS输出图的最短路径
我试图创建一个回溯函数,该函数将返回从根到目标的最短路径的顺序列表 我的路径持有人:Python 带BFS输出图的最短路径,python,algorithm,graph-algorithm,shortest-path,breadth-first-search,Python,Algorithm,Graph Algorithm,Shortest Path,Breadth First Search,我试图创建一个回溯函数,该函数将返回从根到目标的最短路径的顺序列表 我的路径持有人: path_holder = { 'node1':['node2','node3','node5'], 'node2':['node1','node8','node10'], 'node3':['node4','node6']}, 'node4':['node2','node1','node3'], 'node5':['DEADEND'], 'node6':['GOAL']
path_holder = {
'node1':['node2','node3','node5'],
'node2':['node1','node8','node10'],
'node3':['node4','node6']},
'node4':['node2','node1','node3'],
'node5':['DEADEND'],
'node6':['GOAL']
....
}
在我的path_holder输入中,它是BFS的输出,因此第一个节点是根节点,最后一个节点是目标节点。由于path_holder输入是BFS的输出,因此在找到目标时它会停止,因此作为搜索目标所需的先前节点的分支的所有节点也会添加到path_holder
目前,我陷入了while循环,其中出现了无限循环。我的一般策略是从目标节点开始,使用该节点的键查找该键在另一个键的节点列表中的位置。一旦我发现它的列表中包含键的节点,我就将该节点的键设置为新目标。令人困惑的句子对不起
这个图可能包含循环,这可能就是为什么我也得到无限循环的原因
我的回溯功能:
def backtrace(path_holder, root, goal):
dct = {}
for d in path_holder:
dct.update(d)
rootnode = root.keys()[0]
goal = goal.keys()[0]
#x = len(path_holder)
path = []
path.append(goal)
#for i in reversed(xrange(x):
# path_holder[i].keys()
while goal != rootnode:
# find key that contains goal in list
for i in dct:
#print i
for j in dct[i] :
if j not in path:
if j == goal:
path.append(i)
goal = i
# append key that has goal in the list
# set goal to be the key that was appended
# repeat
return path
例:产量
path = ['node1','node3','node6']
1.漏洞
运行您的代码时,出现以下错误:
>>> backtrace(path_holder, 'node1', 'GOAL')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "q20349609.py", line 13, in backtrace
dct.update(d)
ValueError: dictionary update sequence element #0 has length 1; 2 is required
你得到的是字典的钥匙。因此,d接受值'node1','node2'等等,并且不能将它们传递给dict.update方法
但你到底想在这里干什么?如果您试图将path_holder复制到dct中,您可以编写:
dct = dict(path_holder)
但是为什么要费心复印呢?为什么不直接使用路径保持器呢
修复了bug 1后,程序运行时陷入无限循环。这是因为以下几行:
if j not in path:
if j == goal:
path.append(i)
这些线意味着您仅在路径中添加一个节点,如果该节点有一个邻居j,该邻居j尚未在路径中,但等于目标。但等一下,目标已经在这条路上了。因此,这两个条件不能同时满足。因此,不会向路径添加任何内容
显然是这样的:
if j not in path:
应该是:
if i not in path:
因为我是我们正在考虑添加到路径的节点
修复了bug 1和bug 2后,程序取得了一些进展,但仍然陷入无限循环。如果我们在path.appendi之后添加printpath行,那么我们将得到以下输出,直到它卡住为止:
>>> backtrace(path_holder, 'node1', 'GOAL')
['GOAL', 'node6']
['GOAL', 'node6', 'node3']
['GOAL', 'node6', 'node3', 'node4']
您可以看到搜索犯了一个错误:从node3到node4,但除了通过node3的路径外,没有从node4到目标的路径。搜索不会考虑将NODE3添加到路径中,因为它已经存在。
2.该怎么办
当您找到一条到节点4的路径时,您无法知道该节点是否位于从目标到节点1的最短路径上。此时您所能知道的是,如果节点4位于从目标到节点1的最短路径上,那么您将通过节点3到达目标。这就是你必须记录的全部内容
下面是我如何实现这一点的,使用访问的字典为每个节点记录从开始到该节点的最短路径上的前一个节点,并维护一个节点队列,这些节点的邻居我们可能还没有访问过
from collections import deque
class NotFound(Exception): pass
def search(graph, start, goal):
"""Find the shortest path from start to goal in graph (which must be a
map from a node to an iterable of adjacent nodes), using
breadth-first search.
>>> graph = {
... 1: [2, 4, 5],
... 2: [1],
... 3: [4, 6],
... 4: [2, 1, 3],
... 5: [],
... 6: [7],
... 7: [],
... }
>>> search(graph, 1, 7)
[1, 4, 3, 6, 7]
>>> search(graph, 1, 1)
[1]
>>> search(graph, 5, 1) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
NotFound: No path from 5 to 1
"""
visited = {start: None}
queue = deque([start])
while queue:
node = queue.popleft()
if node == goal:
path = []
while node is not None:
path.append(node)
node = visited[node]
return path[::-1]
for neighbour in graph[node]:
if neighbour not in visited:
visited[neighbour] = node
queue.append(neighbour)
raise NotFound('No path from {} to {}'.format(start, goal))
注:
变量path_持有者包含一个称为in的数据结构。所以我称之为变量图
我已经编写了一个docstring来解释函数的作用以及如何调用它。docstring还包含可以使用模块运行的嵌入式代码示例
您的函数从目标向后搜索到开始。但这与从开始到目标的搜索是一样的,所有的边都是反向的。所以我通过向前搜索保持了简单
1.漏洞
运行您的代码时,出现以下错误:
>>> backtrace(path_holder, 'node1', 'GOAL')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "q20349609.py", line 13, in backtrace
dct.update(d)
ValueError: dictionary update sequence element #0 has length 1; 2 is required
你得到的是字典的钥匙。因此,d接受值'node1','node2'等等,并且不能将它们传递给dict.update方法
但你到底想在这里干什么?如果您试图将path_holder复制到dct中,您可以编写:
dct = dict(path_holder)
但是为什么要费心复印呢?为什么不直接使用路径保持器呢
修复了bug 1后,程序运行时陷入无限循环。这是因为以下几行:
if j not in path:
if j == goal:
path.append(i)
这些线意味着您仅在路径中添加一个节点,如果该节点有一个邻居j,该邻居j尚未在路径中,但等于目标。但等一下,目标已经在这条路上了。因此,这两个条件不能同时满足。因此,不会向路径添加任何内容
显然是这样的:
if j not in path:
应该是:
if i not in path:
因为我是我们正在考虑添加到路径的节点
修复了bug 1和bug 2后,程序取得了一些进展,但仍然陷入无限循环。如果我们在path.appendi之后添加printpath行,那么我们将得到以下输出,直到它卡住为止:
>>> backtrace(path_holder, 'node1', 'GOAL')
['GOAL', 'node6']
['GOAL', 'node6', 'node3']
['GOAL', 'node6', 'node3', 'node4']
您可以看到搜索犯了一个错误:从node3到node4,但除了通过node3的路径外,没有从node4到目标的路径。搜索不会考虑将NODE3添加到路径中,因为它已经存在。
2.该怎么办
当您找到一条指向节点4这样的节点的路径时,您无法知道是什么
无论是否存在,该节点都将位于从目标到节点1的最短路径上。此时您所能知道的是,如果节点4位于从目标到节点1的最短路径上,那么您将通过节点3到达目标。这就是你必须记录的全部内容
下面是我如何实现这一点的,使用访问的字典为每个节点记录从开始到该节点的最短路径上的前一个节点,并维护一个节点队列,这些节点的邻居我们可能还没有访问过
from collections import deque
class NotFound(Exception): pass
def search(graph, start, goal):
"""Find the shortest path from start to goal in graph (which must be a
map from a node to an iterable of adjacent nodes), using
breadth-first search.
>>> graph = {
... 1: [2, 4, 5],
... 2: [1],
... 3: [4, 6],
... 4: [2, 1, 3],
... 5: [],
... 6: [7],
... 7: [],
... }
>>> search(graph, 1, 7)
[1, 4, 3, 6, 7]
>>> search(graph, 1, 1)
[1]
>>> search(graph, 5, 1) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
NotFound: No path from 5 to 1
"""
visited = {start: None}
queue = deque([start])
while queue:
node = queue.popleft()
if node == goal:
path = []
while node is not None:
path.append(node)
node = visited[node]
return path[::-1]
for neighbour in graph[node]:
if neighbour not in visited:
visited[neighbour] = node
queue.append(neighbour)
raise NotFound('No path from {} to {}'.format(start, goal))
注:
变量path_持有者包含一个称为in的数据结构。所以我称之为变量图
我已经编写了一个docstring来解释函数的作用以及如何调用它。docstring还包含可以使用模块运行的嵌入式代码示例
您的函数从目标向后搜索到开始。但这与从开始到目标的搜索是一样的,所有的边都是反向的。所以我通过向前搜索保持了简单
你为什么不试试while goal!=rootnode:?两个节点之间的最短路径应该是非循环的,除非你有负的成本优势。@Icardor我认为如果j不在路径中,那么这条线有助于忽略循环,并允许在不陷入循环的情况下进行完整搜索。如果我是,请纠正我wrong@Liondancer传递的输入是什么?@thefourtheye for path_holder这是一个包含列表的字典列表。例如:a=[{'node1':['node2','node3','node5']},{'node2':['node1','node8','node10']},{'node3':['node4','node2']},{'node4':['node2','node1','node3']},{'node5':['DEADEND']},{'node6':['GOAL']}但是我把它们转换成了一本大词典。就根本和目标而言,他们都是保守派。目标的EX是{'nodex':['goal']},根是{'node 1':['node2','node3','node4]}为什么不试试while-goal!=rootnode:?两个节点之间的最短路径应该是非循环的,除非你有负的成本优势。@Icardor我认为如果j不在路径中,那么这条线有助于忽略循环,并允许在不陷入循环的情况下进行完整搜索。如果我是,请纠正我wrong@Liondancer传递的输入是什么?@thefourtheye for path_holder这是一个包含列表的字典列表。例如:a=[{'node1':['node2','node3','node5']},{'node2':['node1','node8','node10']},{'node3':['node4','node2']},{'node4':['node2','node1','node3']},{'node5':['DEADEND']},{'node6':['GOAL']}但是我把它们转换成了一本大词典。就根本和目标而言,他们都是保守派。目标的EX是{'nodex':['goal']},根是{'node 1':['node2','node3','node4]}