Dijkstra:找到目的地时如何设置终止条件?

问题描述

众所周知,Dijkstra找到了从单个源节点到给定图中任何其他节点的最短路径。我尝试修改原始Dijkstra,以找到一对源节点和目标节点之间的最短路径。仅当Dijkstra找到目标节点时,才设置终止程序的终止条件似乎很容易。 但是,我在Python代码中设置的“终止条件”似乎导致了次优的最短路径,而不是最优的最短路径。 Dijkstra代码如下,

def dijkstra(adjList,source,sink):
#define variables
n = len(adjList)    #intentionally 1 more than the number of vertices,keep the 0th entry free for convenience
visited = [False]*n
parent = [-1] *n
#distance = [float('inf')]*n
distance = [1e7]*n
heapNodes = [None]*n
heap = FibonacciHeap()
for i in range(1,n):
    heapNodes[i] = heap.insert(1e7,i)

distance[source] = 0
heap.decrease_key(heapNodes[source],0)

while heap.total_nodes:
    current = heap.extract_min().value
    #print("Current node is: ",current)
    visited[current] = True
    #early exit
    if sink and current == sink:
        break
    for (neighbor,cost) in adjList[current]:
        if not visited[neighbor]:
            if distance[current] + cost < distance[neighbor]:
                distance[neighbor] = distance[current] + cost
                heap.decrease_key(heapNodes[neighbor],distance[neighbor])
                    if  neighbor == sink and current != source:     # this is a wrong logic,since the neighbor may not be selected as the next hop.
                            print("find the sink 1")
                            printSolution(source,sink,distance,parent)
                            break
    if neighbor == sink:
        print("find the sink2")
        break
return distance

adjList = [
[],[[2,7],[3,9],[6,14]],[[1,[4,15],10]],[2,10],11],2]],[5,6]],[[4,6],9]],[[5,[1,14]]
]
dijkstra(adjList,1,4)

邻接表的图形如下所示:

enter image description here

我想找到从节点1到节点4的路径,共有三个路径:

 path 1: 1 --> 2 --> 4              cost: 22
 path 2: 1 --> 2 --> 3 --> 4        cost: 28  
 path 3: 1 --> 3 --> 4              cost: 20
 path 4: 1 --> 3 --> 6 --> 5 --> 4  cost: 26
 path 5: 1 --> 6 --> 3 --> 4        cost: 28
 path 6: 1 --> 6 --> 5 --> 4        cost: 29

Dijkstra最初会选择路径3:1-> 3-> 4,因为它的成本最低。

但是,我修改了终止条件,即当发现当前节点的邻接节点是目的地时,程序将结束。然后得到节点1和节点4之间的路径结果。结果是路径1:1-> 2-> 4。 我分析这是因为我设置了错误的终止条件。当发现当前节点的邻接节点是目标节点时,程序将终止,这是错误的,但是我不知道在找到目标节点时设置合适的终止条件。能否请您提供一些想法?

解决方法

当您刚刚从堆中获得当前节点时,终止条件的唯一正确位置是在外循环的开始。

在迭代邻居时进行该测试是错误的,因为您不能保证最后一条边是最短路径的一部分。试想一下,到邻居的最后一步会付出极高的代价:永远不可能在最短的路径上,所以不要在那儿执行终止条件:可能还有另一条通往水槽的路径更便宜。

我也没有看到您在代码中实际填充parent的位置。

我也不会一开始就将所有节点放在堆上,因为当元素较少时,堆会更快。您可以从只有1个节点的堆开始。

另一种小的优化方法是也使用parent将节点标记为已访问,因此实际上并不需要parentvisited

最后,我不知道FibonacciHeap库,所以我刚刚学习了heapq,这是一个非常轻便的堆实现:

from heapq import heappop,heappush

def dijkstra(adjList,source,sink):
    n = len(adjList)
    parent = [None]*n
    heap = [(0,0)] # No need to push all nodes on the heap at the start
    # only add the source to the heap

    while heap:
        distance,current,came_from = heappop(heap)
        if parent[current] is not None:  # skip if already visited
            continue
        parent[current] = came_from  # this also marks the node as visited
        if sink and current == sink:  # only correct place to have terminating condition
            # build path
            path = [current]
            while current != source:
                current = parent[current]
                path.append(current)
            path.reverse()
            return distance,path
        for (neighbor,cost) in adjList[current]:
            if parent[neighbor] is None:  # not yet visited
                heappush(heap,(distance + cost,neighbor,current))

adjList = [
[],[[2,7],[3,9],[6,14]],[[1,[4,15],10]],[2,10],11],2]],[5,6]],[[4,6],9]],[[5,[1,14]]
]
dist,path = dijkstra(adjList,1,4)
print("found shortest path {},which has a distance of {}".format(path,dist))
,

实际上,您有正确的条件可以退出代码,即current == sink。您不能强加任何其他退出条件。该算法必须一直运行到访问目标节点为止,因为只有在这一点上,您才能确定到目标​​的最短路径的值。由于这种情况,查找单个源单个目标最短路径的复杂性与单个源所有节点最短路径的复杂性相同。因此,您的提前退出条件是正确的,应该删除所有邻居条件检查。