4

我正在尝试计算最短路径。这确实适用于下面粘贴的 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;
    }
}
4

1 回答 1

7

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

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

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


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

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


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


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

于 2012-12-30T19:18:35.997 回答