【发布时间】:2017-09-07 08:37:34
【问题描述】:
我在 Python 中实现了一个简单的图形数据结构,结构如下。此处的代码只是为了阐明函数/变量的含义,但它们非常不言自明,因此您可以跳过阅读。
# Node data structure
class Node:
def __init__(self, label):
self.out_edges = []
self.label = label
self.is_goal = False
def add_edge(self, node, weight = 0):
self.out_edges.append(Edge(node, weight))
# Edge data structure
class Edge:
def __init__(self, node, weight = 0):
self.node = node
self.weight = weight
def to(self):
return self.node
# Graph data structure, utilises classes Node and Edge
class Graph:
def __init__(self):
self.nodes = []
# some other functions here populate the graph, and randomly select three goal nodes.
现在我正在尝试实现一个uniform-cost search(即具有优先级队列的BFS,保证最短路径),它从给定节点v开始,并返回一个最短路径(以列表形式)到其中一个三个目标节点。 目标节点是指属性is_goal 设置为true 的节点。
这是我的实现:
def ucs(G, v):
visited = set() # set of visited nodes
visited.add(v) # mark the starting vertex as visited
q = queue.PriorityQueue() # we store vertices in the (priority) queue as tuples with cumulative cost
q.put((0, v)) # add the starting node, this has zero *cumulative* cost
goal_node = None # this will be set as the goal node if one is found
parents = {v:None} # this dictionary contains the parent of each node, necessary for path construction
while not q.empty(): # while the queue is nonempty
dequeued_item = q.get()
current_node = dequeued_item[1] # get node at top of queue
current_node_priority = dequeued_item[0] # get the cumulative priority for later
if current_node.is_goal: # if the current node is the goal
path_to_goal = [current_node] # the path to the goal ends with the current node (obviously)
prev_node = current_node # set the previous node to be the current node (this will changed with each iteration)
while prev_node != v: # go back up the path using parents, and add to path
parent = parents[prev_node]
path_to_goal.append(parent)
prev_node = parent
path_to_goal.reverse() # reverse the path
return path_to_goal # return it
else:
for edge in current_node.out_edges: # otherwise, for each adjacent node
child = edge.to() # (avoid calling .to() in future)
if child not in visited: # if it is not visited
visited.add(child) # mark it as visited
parents[child] = current_node # set the current node as the parent of child
q.put((current_node_priority + edge.weight, child)) # and enqueue it with *cumulative* priority
现在,经过大量测试并与其他算法进行比较,这个实现似乎运行良好 - 直到我用这张图尝试它:
无论出于何种原因,ucs(G,v) 返回的路径 H -> I 的成本为 0.87,而路径 H -> F -> I 的成本为 0.71(此路径是通过运行 DFS 获得的)。下图也给出了错误的路径:
算法给出了G -> F而不是G -> E -> F,由DFS再次获得。在这些罕见的情况下,我能观察到的唯一模式是所选目标节点总是有一个循环。我不知道出了什么问题。任何提示将不胜感激。
【问题讨论】:
-
在您实际访问某个节点之前以及在您确定在那里找到最便宜的路径之前,您会认为该节点已“访问过”。
-
... 扩展:如果有两个路径到一个节点,你只考虑其中一个,因为你在找到第一个路径时标记了一个访问过的节点,而不检查是否没有另一条(更便宜的)路径。这也与“父节点”冲突,其中每个节点只有一个父节点,只有当它是最便宜路径上的父节点时才可以
-
我明白你的意思...但是在我给出的第一个示例中,如果算法要选择最便宜的路径,为什么要选择路径
H -> I?优先级队列排名不应该解决这个问题吗?我将如何修复访问/父对象? -
“为什么算法选择路径 H -> I,如果它是为了选择最便宜的路径” - 因为它在 dhke 和我刚才描述的方式中是错误的。如果事情总是按照他们应该做的而不是你实际编写的,编程会容易得多。
-
这与您的问题无关,但
queue模块中的类会执行一些您不需要的线程同步操作。对于仅在一个线程中使用的基本优先级队列,请改用heapq(这是queue.PriorityQueue在内部用于其实现的内容)。
标签: python algorithm search graph