【问题标题】:How to modify dijkstra algorithm to find all possible paths?如何修改 dijkstra 算法以找到所有可能的路径?
【发布时间】:2013-04-14 18:55:56
【问题描述】:

我知道以前可以问过,但我找不到。 我需要修改下面的 dijkstra 算法,该算法适用于查找 2个节点之间的最短路径,但我还需要找到所有可能的路径。 我知道这样做应该相对容易,但到目前为止我不知道 如何做到这一点最简单的方法。我正在使用有向加权图。

    class Dijkstra
    {
        private List<Node> _nodes;
        private List<Edge> _edges;
        private List<Node> _basis;
        private Dictionary<string, double> _dist;
        private Dictionary<string, Node> _previous;

        public Dijkstra(List<Edge> edges, List<Node> nodes)
        {

            Edges = edges;
            Nodes = nodes;
            Basis = new List<Node>();
            Dist = new Dictionary<string, double>();
            Previous = new Dictionary<string, Node>();

            // record node 
            foreach (Node n in Nodes)
            {
                Previous.Add(n.Name, null);
                Basis.Add(n);
                Dist.Add(n.Name, double.MaxValue);
            }
        }

        /// Calculates the shortest path from the start
        ///  to all other nodes
        public void calculateDistance(Node start)
        {
            Dist[start.Name] = 0;

            while (Basis.Count > 0)
            {
                Node u = getNodeWithSmallestDistance();
                if (u == null)
                {
                    Basis.Clear();
                }
                else
                {
                    foreach (Node v in getNeighbors(u))
                    {
                        double alt = Dist[u.Name] + getDistanceBetween(u, v);
                        if (alt < Dist[v.Name])
                        {
                            Dist[v.Name] = alt;
                            Previous[v.Name] = u;
                        }
                    }
                    Basis.Remove(u);
                }
            }
        }

        public List<Node> getPathTo(Node d)
        {
            List<Node> path = new List<Node>();

            path.Insert(0, d);

            while (Previous[d.Name] != null)
            {
                d = Previous[d.Name];
                path.Insert(0, d);
            }

            return path;
        }

    public Node getNodeWithSmallestDistance()
        {
            double distance = double.MaxValue;
            Node smallest = null;

            foreach (Node n in Basis)
            {
                if (Dist[n.Name] < distance)       
                {
                    distance = Dist[n.Name];
                    smallest = n;
                }
            }

            return smallest;
        }


        public List<Node> getNeighbors(Node n)
        {
            List<Node> neighbors = new List<Node>();

            foreach (Edge e in Edges)
            {
                if (e.Origin.Equals(n) && Basis.Contains(n))
                {
                    neighbors.Add(e.Destination);
                }
            }

            return neighbors;
        }

       public double getDistanceBetween(Node o, Node d)
        {
            foreach (Edge e in Edges)
            {
                if (e.Origin.Equals(o) && e.Destination.Equals(d))
                {
                    return e.Distance;
                }
            }

            return 0;
        }


        public List<Node> Nodes
        {
            get { return _nodes; }
            set { _nodes = value; }
        }

        public List<Edge> Edges
        {
            get { return _edges; }
            set { _edges = value; }
        }

        public List<Node> Basis
        {
            get { return _basis; }
            set { _basis = value; }
        }

        public Dictionary<string, double> Dist
        {
            get { return _dist; }
            set { _dist = value; }
        }

        public Dictionary<string, Node> Previous
        {
            get { return _previous; }
            set { _previous = value; }
        }
    }
}

static void Main(string[] args)
        {
//Nodes initialisation goes here

Dijkstra d = new Dijkstra(_edges, _nodes);
d.calculateDistance(_dictNodes["A"]);
 List<Node> path = d.getPathTo(_dictNodes["C"]);
}

【问题讨论】:

  • 我已经编辑了你的标题。请参阅“Should questions include “tags” in their titles?”,其中的共识是“不,他们不应该”。
  • 你知道这个算法是如何工作的吗?一旦你这样做了,就更容易找到如何改变它,返回所有可能的路径。

标签: c# algorithm graph-algorithm breadth-first-search dijkstra


【解决方案1】:

好的,我实际上已经修改了 Dijkstra 类来执行 BFS,它为我提供了所有可能的路线。我添加了这个方法:

public void BreadthFirst(Edge graph, LinkedList<String> visited) 
{
    LinkedList<String> nodes = graph.adjacentNodes(visited.Last());

    // Examine adjacent nodes
    foreach (String node in nodes) 
    {
        if (visited.Contains(node))
        {
            continue;
        }

        if (node.Equals(endNode))
        {
            visited.AddLast(node);

            printPath(visited);

            visited.RemoveLast();

            break;
         }
     }

    // In breadth-first, recursion needs to come after visiting adjacent nodes
    foreach (String node in nodes)
    {      
        if(visited.Contains(node) || node.Equals(endNode))
        {
            continue;
        }

        visited.AddLast(node);

        // Recursion
        BreadthFirst(graph, visited);

        visited.RemoveLast();
    }
}

用法如下:

Dijkstra d = new Dijkstra(_edges, _nodes);

LinkedList<String> visited = new LinkedList<string>();  //collecting all routes
visited.AddFirst(start);

d.BreadthFirst(graph, visited);

【讨论】:

  • 你对祖鲁人帮助很大。我能够使您的代码适应我现有的代码需求。非常感谢! :) 继续分享人...
  • 我正在评论一篇非常古老的帖子,但这是一个快速的基本问题。在您修改后的新方法 - 广度优先中,图形变量是否包含邻接列表节点?如果我理解正确,那么 graph.adjacentNodes(visited.Last()) 将简单地给出与当前节点相邻的所有节点,如邻接列表表示所提供的那样。如果是,那么这种方法从 Dijkstra 算法中获得了什么?如果我没有忽略某些东西,输入似乎是邻接图而不是 Dijkstra 的输出?
【解决方案2】:

你不能轻易地修改 Dijkstra 来显示所有可能的路径。您需要修改 BFS 或 DFS 搜索。

如果您尝试修改 Dijkstra,最终会以 BFS 或 DFS 搜索算法结束,因此最好从那里开始。

【讨论】:

  • 好吧,在下面的代码部分中,我在 Dist[] 中的邻居之间拥有所有可能的路线,但该程序需要诅咒最小的路线,所以我想也许在这里一些聪明的技巧可以完成这项工作。公共节点 getNodeWithSmallestDistance() { double distance = double.MaxValue;节点最小 = null; foreach(基础中的节点 n){ if (Dist[n.Name]
【解决方案3】:

如果您想找到所有简单路径,请使用修改后的BFS(您会记住使用过的顶点,以免在路径中重复它们)。甚至不可能找到所有路径(过程不会终止(即它不是算法))。想象一下带有循环的图,所有节点之间都有无限的路径(包含的循环数量不同......)

【讨论】:

    【解决方案4】:

    以下是我在网上找到的几种算法,用于查找图中所有可能的路径。他们没有修改 Dijkstra 的算法,但我认为他们应该做你想做的事。


    来自https://mathoverflow.net/questions/18603/finding-all-paths-on-undirected-graph

    Suresh 建议使用 DFS,MRA 指出目前尚不清楚是否可行。这是我在该 cmets 线程之后尝试的解决方案。如果图有 m 条边、n 个节点和从源 s 到目标 t 的 p 条路径,那么下面的算法会在 O((np+1)(m+n)) 时间内打印所有路径。 (特别是注意到没有路径需要 O(m+n) 时间。)

    这个想法很简单:进行详尽的搜索,但如果陷入困境,请尽早放弃。

    如果不提前放弃,MRA 的反例表明,即使 p=1,穷举搜索也会花费 Ω(n!) 时间:节点 t 只有一条相邻边,其邻居是节点 s,它是完整 (子)图Kn-1。

    将 s 压入路径堆栈并调用 search(s):

    path // is a stack (initially empty)
    seen // is a set
    
    def stuck(x)
       if x == t
         return False
       for each neighbor y of x
         if y not in seen
           insert y in seen
           if !stuck(y)
             return False
       return True
    
    def search(x)
      if x == t
        print path
      seen = set(path)
      if stuck(x)
        return
      for each neighbor y of x
        push y on the path
        search(y)
        pop y from the path
    

    这里的搜索是穷举搜索,卡住可以用 DFS 风格(如这里)或 BFS 风格实现。


    来自All possible paths in a cyclic undirected graph

    您可以像 |Vlad 描述的那样使用 DFS 找到所有路径。要查找每个路径中出现的节点,您只需维护一个布尔数组,该数组表示到目前为止每个节点是否出现在每个路径中。当您的 DFS 找到路径时,遍历不在路径中的每个顶点并将相应的数组值设置为 false。完成后,只有值为 true 的顶点才会出现在每条路径中。

    伪代码:

    int source;
    int sink;
    int nVerts;
    bool inAllPaths[nVerts]; // init to all true
    bool visited[nVerts]; // init to all false
    stack<int> path; // init empty
    
    bool dfs(int u)
      if (visited[u])
        return;
      if (u == sink)
        for i = 0 to nVerts-1
          if !stack.contains(i)
            inAllPaths[i] = false;
        return true;
      else
        visited[u] = true;
        stack.push(u);
        foreach edge (u, v)
          dfs(v);
        stack.pop();
        visited[u] = false;
        return false;
    
    
    main()
      dfs(source);
      // inAllPaths contains true at vertices that exist in all paths
      // from source to sink.
    

    但是,这种算法效率不高。例如,在一个包含 n 个顶点(所有顶点与所有其他顶点之间都有边)的完整图中,路径的数量将是 n! (n 阶乘)。

    更好的算法是分别检查每个顶点的每条路径中是否存在。对于每个顶点,尝试找到从源到汇的路径,而不是去那个顶点。如果找不到,那是因为顶点出现在每条路径中。

    伪代码:

    // Using the same initialisation as above, but with a slight modification
    // to dfs: change the foreach loop to
    foreach edge (u, v)
      if (dfs(v))
        return true; // exit as soon as we find a path
    
    main()
      for i = 0 to nVerts-1
        set all visited to false;
        if (inAllPaths[i])
          visited[i] = true;
          if (dfs(source))
            inAllPaths[i] = false;
          visited[i] = false;
    

    不幸的是,在搜索路径时,这仍然是指数级的最坏情况。您可以通过将搜索更改为广度优先搜索来解决此问题。如果我没记错的话,这应该会给你 O(VE) 性能。


    其他一些讨论这个问题的文章:

    algorithm to enumerate all possible paths
    Find all paths between two graph nodes

    【讨论】:

      【解决方案5】:

      您可以通过以下方式使用 BFS:以下 (python) 函数(使用 递归寻路 修改 BFS > 两个节点之间的函数)可用于在 非循环图中找到两个节点之间的所有可能路径

      from collections import defaultdict
      
      # modified BFS
      def find_all_parents(G, s):
          Q = [s]
          parents = defaultdict(set)
          while len(Q) != 0:
              v = Q[0]
              Q.pop(0)
              for w in G.get(v, []):
                  parents[w].add(v)
                  Q.append(w) 
          return parents
      
      # recursive path-finding function (assumes that there exists a path in G from a to b)   
      def find_all_paths(parents, a, b): 
          return [a] if a == b else [y + b for x in list(parents[b]) for y in find_all_paths(parents, a, x)]
      

      例如,用下面的图(DAG)G 给出

      G = {'A':['B','C'], 'B':['D'], 'C':['D', 'F'], 'D':['E', 'F'], 'E':['F']}
      

      如果我们要查找节点'A''F'之间的所有路径(使用上面定义的函数为find_all_paths(find_all_parents(G, 'A'), 'A', 'F')),它将返回以下路径:

      【讨论】:

        猜你喜欢
        • 2011-02-18
        • 1970-01-01
        • 1970-01-01
        • 2021-03-07
        • 1970-01-01
        • 2021-10-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多