在使用Dijkstra算法时,检查多次访问的节点是否必须?

时间:2016-01-20 23:04:26

标签: python algorithm dijkstra shortest-path

我和我的同事正在讨论Dijkstra算法的实现。以下是使用Python的实现:

def dijkstra(self, origin, destination):
        """Use Dijkstra's algorithm to find the cheapest path."""

        routes = Heap()
        for neighbor in self.neighbors(origin):
            price = self.get_price(origin, neighbor)
            routes.push(Route(price=price, path=[origin, neighbor]))

        visited = set()
        visited.add(origin)

        while routes:

            # Find the nearest yet-to-visit airport
            price, path = routes.pop()
            airport = path[-1]
            if airport in visited:
                continue

            # We have arrived! Wo-hoo!
            if airport is destination:
                return price, path

            # Tentative distances to all the unvisited neighbors
            for neighbor in self.neighbors(airport):
                if neighbor not in visited:
                    # Total spent so far plus the price of getting there
                    new_price = price + self.get_price(airport, neighbor)
                    new_path  = path  + [neighbor]
                    routes.push(Route(new_price, new_path))

            visited.add(airport)

        return float('infinity')

这里有争议的路线是:

if neighbor not in visited:

我的观点是这一行必须用

之类的东西替换
if neighbor not in visited or new_price < cost_so_far[neighbor]

在我发现的算法的所有实现中,当您到达节点的成本低于节点的当前成本时,必须检查案例。例如,来自Wikipedia第17行和第18行伪码

1  function Dijkstra(Graph, source):
2      dist[source] ← 0                                    // Initialization
3
4      create vertex set Q
5
6      for each vertex v in Graph:           
7          if v ≠ source
8              dist[v] ← INFINITY                          // Unknown distance from source to v
9              prev[v] ← UNDEFINED                         // Predecessor of v
10
11         Q.add_with_priority(v, dist[v])
12
13
14     while Q is not empty:                              // The main loop
15         u ← Q.extract_min()                            // Remove and return best vertex
16         for each neighbor v of u:                       // only v that is still in Q
17             alt = dist[u] + length(u, v) 
18             if alt < dist[v]
19                 dist[v] ← alt
20                 prev[v] ← u
21                 Q.decrease_priority(v, alt)
22
23     return dist[], prev[]

问题是:我的同事的实施是否正确或是否应修改代码以检查您是否以低于当前价格的价格与某个邻居联系?

注意:以下是我同事实施的source code

1 个答案:

答案 0 :(得分:3)

所以,问题是代码中的行是否

if neighbor not in visited or new_price < cost_so_far[neighbor]:

可以或必须由

替换
new_price < cost_so_far[neighbor]

答案是:可以,是的;必须,不。 添加neighbor not in visited不会更改算法流程中的任何内容,每次airport为false时都会为false。

原因是Dijkstra算法的工作原理。 从本质上讲,它构建了一条最短路径的树。 只要将visited添加到airport,就会认为它位于树中:此时,算法已找到此x的最短路径。

假设在步骤A,我们将某个机场visited添加到y > x。 进一步假设在步骤cost_so_farA到机场new_price = price + self.get_price(airport, neighbor)减少了。 怎么会减少? 这将要求某些price小于步骤y中的routes。 现在回想一下,price是一个优先级队列,因此它以非递减顺序提供new_price个。 图的边缘也是非负的(否则,Dijkstra的算法确实会产生错误的结果并且不适用)。 因此,我们达成了一个矛盾:新if airport in visited: continue至少是旧价格,但结果却低于此价格。

混淆的根源可能是实现的主循环逐个考虑某些路由。 实质上,这些路线对应于图的边缘。 所以,可以有| E |路线,但只有| V |他们将被接受,其他所有人都将失败airport。 如果我们实现算法,以便主循环的每次迭代恰好将visited添加到{{1}}(恰好是最短路径树的一个顶点),整个事情可能会变得更加清晰。