【问题标题】:Updating priority queue python Dijkstras algorithm更新优先级队列python Dijkstras算法
【发布时间】:2019-07-27 17:37:20
【问题描述】:

我想在下面的 WORKING AND FINISHED 代码中理解,为什么在更新 pq_update 时,它​​被写成 pq_update[neighbour][1]。

而不是写 pq_update[neighbour] (我就是这样做的),它似乎没有改变任何东西,所以为什么要包含它?

谢谢

import heapq

def dijkstra(graph, start):

  distances = {vertex:float('inf') for vertex in graph}
  pq = []
  pq_update = {}
  distances[start] = 0

  for vertex, value in distances.items():
    entry = [vertex, value]
    heapq.heappush(pq, entry)
    pq_update[vertex] = entry

  while pq:

    getmin = heapq.heappop(pq)[0]

    for neighbour, distance_neigh in graph[getmin].items():
      dist = distances[getmin] + distance_neigh
      if dist < distances[neighbour]:
        distances[neighbour] = dist
        pq_update[neighbour][1] = dist # THIS LINE !!!

  print(distances)
  return distances 


if __name__ == '__main__':

  example_graph = {
      'U': {'V': 2, 'W': 5, 'X': 1},
      'V': {'U': 2, 'X': 2, 'W': 3},
      'W': {'V': 3, 'U': 5, 'X': 3, 'Y': 1, 'Z': 5},
      'X': {'U': 1, 'V': 2, 'W': 3, 'Y': 1},
      'Y': {'X': 1, 'W': 1, 'Z': 1},
      'Z': {'W': 5, 'Y': 1},
  }
  dijkstra(example_graph, 'X')

【问题讨论】:

  • 那段代码对我来说似乎有问题。堆首先按节点名称排序,然后仅按距离排序。但是,如果您将其更改为正确排序,当您修改堆中现有节点的条目时,您可能会破坏堆属性,因此在进行任何更改后确实应该调用heapify 或类似的方法来修复它。跨度>

标签: python dictionary graph


【解决方案1】:

注意:您的实现已损坏,无法正确实现 Dijkstra。更多内容如下。

pq_update 字典包含 列表,每个列表都有两个条目:

for vertex, value in distances.items():
    entry = [vertex, value]
    heapq.heappush(pq, entry)
    pq_update[vertex] = entry

所以pq_update[neighbour] 是一个包含顶点和距离的列表。您要更新距离,而不是替换[vertex, value] 列表,因此使用pq_update[neighbour][1]

请注意,entry 列表也与heapq共享pq 堆引用了相同的列表对象,因此对 pq_update[neightbor][1] 的更改也将在仍待在堆上处理的条目中可见!

当您直接分配给pq_update[neighbour] 时,您删除该连接。

您看不到任何差异的原因是因为该算法的实现实际上是损坏,因为堆没有正确使用。堆首先按您推入的列表项中的第一个值排序。在您的代码中是节点 name,而不是距离,并且项的 heapq 顺序永远不会更新 当列表项中的距离改变时。因为heapq没有正确使用,所以总是按字母顺序遍历节点。

要正确使用heapq,您需要将边长放在首位,并且不要更改堆上的值;如果您使用元组,您不会意外地这样做。实际上,您只需要将节点推送到您到达的堆上;您最终会得到一些节点的多个条目(通过多个路径到达),但heapq 仍将首先显示到该节点的最短路径。只需保留一组访问过的节点,以便您知道跳过任何更长的路径。关键是您在较长路径之前访问到给定节点的较短路径,并且您不需要就地更改 heapq 项来实现这一点。

您可以将您的函数(使用更好的变量名)重写为:

def dijkstra(graph, start):
    """Visit all nodes and calculate the shortest paths to each from start"""
    queue = [(0, start)]
    distances = {start: 0}
    visited = set()

    while queue:
        _, node = heapq.heappop(queue)  # (distance, node), ignore distance
        if node in visited:
            continue
        visited.add(node)
        dist = distances[node]

        for neighbour, neighbour_dist in graph[node].items():
            if neighbour in visited:
                continue
            neighbour_dist += dist
            if neighbour_dist < distances.get(neighbour, float('inf')):
                heapq.heappush(queue, (neighbour_dist, neighbour))
                distances[neighbour] = neighbour_dist

    return distances

【讨论】:

  • 您好,谢谢,上面的代码严重损坏,我已经修复它并最终完全删除了 pq_update。
  • @justanothertechdude:是的,因为您实际上并没有找到到目标节点的最短路径。您正在对整个图表进行一次呼吸优先的全遍历。
  • 我可能会遗漏一些东西,但是如果我们在 this answer 的图表上运行这个算法呢?似乎如果我们从节点 0 开始并且不在队列中保持优先级,我们将得到不正确的到节点 3 的距离。
  • @kazarey:是的,我这里的实现需要使用优先级队列,这确实是错误的,那篇文章很好地说明了原因。
  • @kazarey:感谢您指出这一点,我已经更新了代码以正确使用 heapq。
猜你喜欢
  • 2013-12-23
  • 2010-10-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-05
  • 2013-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多