Dijkstra Shortest-Paths 快速重新计算,如果只有一条边被移除

Posted

技术标签:

【中文标题】Dijkstra Shortest-Paths 快速重新计算,如果只有一条边被移除【英文标题】:Dijkstra Shortest-Paths fast recalculation if only an edge got removed 【发布时间】:2012-12-30 05:11:46 【问题描述】:

我正在尝试计算最短路径。这确实适用于下面粘贴的 Dijkstra 实现。但是我想加快速度。

我使用这个实现来决定接下来我想去哪个领域。该图表示一个二维数组,其中所有字段都连接到每个邻居。但随着时间的推移,会发生以下情况:我需要移除一些边缘(有障碍物)。起始节点是我当前的位置,它也会随着时间而改变。

这意味着:

我从不添加节点,从不添加新边,从不更改边的权重。唯一的操作是删除一条边

起始节点确实会随时间变化

问题:

当我知道图中唯一的变化是删除一条边时,是否有一种算法可以快速重新计算最短路径?

当起始节点仅更改为它的一个邻居时,是否有一种算法可以让我快速重新计算最短路径?

其他算法可能更适合我的问题吗?

感谢您的帮助

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;

public class Dijkstra<T>

    private Node<T> calculatedStart;

    private ReadOnlyCollection<Node<T>> Nodes 
        get ;
        set ;
    

    private ReadOnlyCollection<Edge<T>> Edges 
        get;
        set;
    

    private List<Node<T>> NodesToInspect 
        get;
        set ;
    

    private Dictionary<Node<T>, int> Distance 
        get ;
        set ;
    

    private Dictionary<Node<T>, Node<T>> PreviousNode 
        get;
        set ;
    

    public Dijkstra (ReadOnlyCollection<Edge<T>> edges, ReadOnlyCollection<Node<T>> nodes)
    
        Edges = edges;
        Nodes = nodes;
        NodesToInspect = new List<Node<T>> ();
        Distance = new Dictionary<Node<T>, int> ();
        PreviousNode = new Dictionary<Node<T>, Node<T>> ();

        foreach (Node<T> n in Nodes) 
            PreviousNode.Add (n, null);
            NodesToInspect.Add (n);
            Distance.Add (n, int.MaxValue);
        
    

    public LinkedList<T> GetPath (T start, T destination)
    
        Node<T> startNode = new Node<T> (start);
        Node<T> destinationNode = new Node<T> (destination);

        CalculateAllShortestDistances (startNode);

        // building path going back from the destination to the start always taking the nearest node
        LinkedList<T> path = new LinkedList<T> ();
        path.AddFirst (destinationNode.Value);

        while (PreviousNode[destinationNode] != null) 
            destinationNode = PreviousNode [destinationNode];
            path.AddFirst (destinationNode.Value);
        

        path.RemoveFirst ();

        return path;
    

    private void CalculateAllShortestDistances (Node<T> startNode)
    
        if (startNode.Value.Equals (calculatedStart)) 
            return;
        

        Distance [startNode] = 0;

        while (NodesToInspect.Count > 0) 
            Node<T> nearestNode = GetNodeWithSmallestDistance ();
            // if we cannot find another node with the function above we can exit the algorithm and clear the
            // nodes to inspect because they would not be reachable from the start or will not be able to shorten the paths...
            // this algorithm does also implicitly kind of calculate the minimum spanning tree...
            if (nearestNode == null) 
                NodesToInspect.Clear ();
             else 
                foreach (Node<T> neighbour in GetNeighborsFromNodesToInspect(nearestNode)) 
                    // calculate distance with the currently inspected neighbour
                    int dist = Distance [nearestNode] + GetDirectDistanceBetween (nearestNode, neighbour);

                    // set the neighbour as shortest if it is better than the current shortest distance
                    if (dist < Distance [neighbour]) 
                        Distance [neighbour] = dist;
                        PreviousNode [neighbour] = nearestNode;
                    
                
                NodesToInspect.Remove (nearestNode);
            
        

        calculatedStart = startNode;
    

    private Node<T> GetNodeWithSmallestDistance ()
    
        int distance = int.MaxValue;
        Node<T> smallest = null;

        foreach (Node<T> inspectedNode in NodesToInspect) 
            if (Distance [inspectedNode] < distance) 
                distance = Distance [inspectedNode];
                smallest = inspectedNode;
            
        

        return smallest;
    

    private List<Node<T>> GetNeighborsFromNodesToInspect (Node<T> n)
    
        List<Node<T>> neighbors = new List<Node<T>> ();

        foreach (Edge<T> e in Edges) 
            if (e.Start.Equals (n) && NodesToInspect.Contains (n)) 
                neighbors.Add (e.End);
            
        

        return neighbors;
    

    private int GetDirectDistanceBetween (Node<T> startNode, Node<T> endNode)
    
        foreach (Edge<T> e in Edges) 
            if (e.Start.Equals (startNode) && e.End.Equals (endNode)) 
                return e.Distance;
            
        

        return int.MaxValue;
    

【问题讨论】:

您是否看过另一个关于动态更新最短路径的问题 (***.com/questions/6760163/…)?无论如何,Dijkstra 是一种单源最短路径算法,听起来不像你想要的。 【参考方案1】:

当我知道图中唯一的变化是删除一条边时,是否有一种算法可以快速重新计算最短路径?

当起始节点仅更改为它的一个邻居时,是否有一种算法可以让我快速重新计算最短路径?

这两个问题的答案都是肯定的。


对于第一种情况,您要查找的算法称为LPA*(有时,不太常见,称为增量 A*。该论文上的标题已过时)。这是对 A* 的(相当复杂的)修改,允许在只有少数边发生变化时快速重新计算最佳路径。

与 A* 一样,LPA* 需要 admissible distance heuristic。如果不存在这样的启发式,您可以将其设置为 0。在 A* 中执行此操作实际上会将其变为 Djikstra 算法;在 LPA* 中执行此操作会将其变成一种晦涩难懂、很少使用的算法,称为 DynamicSWSF-SP。


对于第二种情况,您正在寻找D*-Lite。这是对 LPA* 的一个非常简单的修改(很简单,至少,一旦你理解了 LPA*),它会在单元从开始到结束移动并获得新信息时进行增量寻路 (添加/删除/更改边缘)。它主要用于穿越未知或部分已知地形的机器人。


我已经写了一个相当全面的答案(带有论文链接,在问题中)关于各种寻路算法here。

【讨论】:

以上是关于Dijkstra Shortest-Paths 快速重新计算,如果只有一条边被移除的主要内容,如果未能解决你的问题,请参考以下文章

训练指南 UVA - 11374(最短路Dijkstra + 记录路径 + 模板)

关于dijkstra算法的一点理解

POJ-1511(Dijkstra+优先队列优化)

关于Dijkstra、SPFA、Bellman-Ford、Floyed算法的问题

UVa 11374 机场快线

算法之最短路