简体   繁体   English

计算图中从任何给定顶点到另一个顶点的所有可能路径的正确算法是什么?

[英]What's a proper algorithm to count all possible paths from any given vertex to another in a graph?

I'm working with graphs and I'd like to count all possible paths from given vertex X to given vertex Y.我正在使用图表,我想计算从给定顶点 X 到给定顶点 Y 的所有可能路径。

This is the algorithm I've come up with:这是我想出的算法:

class Graph
  def paths_from(vertex_a, vertex_b, count = 0, visited = Array.new(@vertices.length, false))
    return count if @vertices.none?(vertex_b) || @vertices.none?(vertex_a)

    count += 1 if vertex_a == vertex_b
    visited[@vertices.index(vertex_a)] = true
    @net[@vertices.index(vertex_a)].each do |vertex|
      paths_from(vertex, vertex_b, count, visited) unless visited[@vertices.index(vertex)]
    end

    count
  end
end

Using recursion, I'm expecting to traverse df through the graph.使用递归,我期望通过图形遍历 df 。 However, I keep getting 0 instead of the expected value given below graph:但是,我不断得到 0 而不是下图给出的预期值:

describe Graph do
  context 'can output all possible from vertex a to vertex b.' do
    let(:subject) { Graph.new(%w[a b c d e]) }
    before(:each) do
      subject.add_edge(0, 1)
      subject.add_edge(0, 2)
      subject.add_edge(0, 4)
      subject.add_edge(1, 2)
      subject.add_edge(1, 4)
      subject.add_edge(2, 3)
      subject.add_edge(3, 1)
    end
  
    it 'example #1' do
      expect(subject.paths_from('a', 'f')).to eql 0 # => should output 0 and it does.
    end

    it 'example #2' do
      expect(subject.paths_from('f', 'a')).to eql 0 # => should ouput 0 and it does.
    end

    it 'example #3' do
      expect(subject.paths_from('a', 'b')).to eql 2
    end
  end
end

Doubt #1: I've checked geeksforgeeks approach tips regarding the algorithm: it states I should backtrack.疑点 1:我检查了 geeksforgeeks 关于算法的方法提示:它表明我应该回溯。 What is that and how may I do it?那是什么,我该怎么做? I guess they're referencing the visited variable... but I've got no clue as to how to do that.我猜他们正在引用访问的变量......但我不知道如何做到这一点。

I'll drop the class definition just in case.为了以防万一,我将放弃 class 定义。

class Graph
attr_accessor :net, :vertices

  def initialize(vertices = [])
    @net = Array.new(vertices.length) { [] }
    @vertices = vertices
  end

  def add_edge(vertex_a, vertex_b)
    return if @net[vertex_a].nil? || @vertices[vertex_b].nil?

    @net[vertex_a] << @vertices[vertex_b]
  end
end

Doubt #2: If I print the count variable right before the @net loop, it prints 0, then '1' four times, yet it returns 0. Why is that?疑问 #2:如果我在 @net 循环之前打印 count 变量,它会打印 0,然后打印 '1' 四次,但它返回 0。为什么会这样? I suppose it's because it's returing #paths_from's first call... if that's the case, how may I return #paths_from's last call's count variable?我想这是因为它正在返回#paths_from 的第一次调用......如果是这种情况,我该如何返回#paths_from 的最后一次调用的计数变量?

You don't appear to be recording the output of the recursion.您似乎没有记录递归的 output 。

can you try something like:你可以试试这样的东西:

count += paths_from(vertex, vertex_b, count, visited) unless visited[@vertices.index(vertex)]

not passing in a count at all:根本没有传递计数:

class Graph
attr_accessor :net, :vertices, :visited

  def initialize(vertices = [])
    @net = Array.new(vertices.length) { [] }
    @visited = Array.new(vertices.length, false)
    @vertices = vertices
  end

  def add_edge(vertex_a, vertex_b)
    return if @net[vertex_a].nil? || @vertices[vertex_b].nil?

    @net[vertex_a] << @vertices[vertex_b]
  end

  def paths_from(vertex_a, vertex_b)
    return 0 if @vertices.none?(vertex_b) || @vertices.none?(vertex_a)
    count = 0

    count += 1 if vertex_a == vertex_b
    @visited[@vertices.index(vertex_a)] = true
    @net[@vertices.index(vertex_a)].each do |vertex|
      count += paths_from(vertex, vertex_b) unless @visited[@vertices.index(vertex)]
    end

    count
  end
end

I assume the graph is directed and contains no cycles.我假设该图是有向的并且不包含循环。

Suppose the graph is described by the following hash.假设该图由以下 hash 描述。

graph = { :A=>[:C, :D], :B=> [:D], :C=>[:D, :E, :F], :D=>[:G], :E=>[:F, :H],
          :F=>[:D, :G, :I, :H], :G=>[:H, :I], :H=>[], :I=>[] } 

The nodes are keys and the arcs are given by the keys and each element of a keys value.节点是键,弧由键和键值的每个元素给出。 There are, for example, arcs :A->:C , :A-->:D , :B->:D and so on.例如,圆弧:A->:C , :A-->:D , :B->:D等等。

We can display this graph as follows.我们可以如下显示这个图表。

在此处输入图像描述

Given two of the nodes, designated as the origin and terminus , the problem is to determine the number of paths from origin to terminus .给定两个节点,指定为originterminus ,问题是确定从originterminus的路径数。

Suppose认为

origin   = :A
terminus = :H

It is seen that there are nine paths from A to H:可以看出,从 A 到 H 有 9 条路径:

A-C-E-H
A-C-E-F-H
A-C-E-F-G-H
A-C-E-F-D-G-H
A-C-F-H
A-C-F-D-G-H
A-C-F-G-H
A-C-D-G-H
A-D-G-H

I will give two solutions.我将给出两个解决方案。 The first is a recursion that requires little code but enumerates all paths.第一个是需要很少代码但枚举所有路径的递归。 The number of such paths, however, can grow exponentially with the number of nodes.然而,这样的路径的数量可以随着节点的数量呈指数增长。 The second is more complex but is much faster for larger graphs.第二个更复杂,但对于较大的图来说要快得多。 It's computational complexity appears to be only O(n 2 ), where n is the number of nodes.它的计算复杂度似乎只有 O(n 2 ),其中n是节点数。

Enumerate paths from origin to destination枚举从origindestination的路径

def tot_paths(graph, terminus, node)
  graph[node].reduce(0) do |tot, n|
    tot + ((n == terminus) ? 1 : tot_paths(graph, terminus, n))
  end
end
tot_paths(graph, :H, :A)
  #=> 9

More complex, but a much more efficient solution更复杂但更有效的解决方案

The second approach requires two steps.第二种方法需要两个步骤。 The first is to perform a topological sort of the nodes of the graph.首先是对图的节点进行拓扑排序

Any array sorted that is an array of topologically-sorted nodes has the property that, for any pair of nodes ni = sorted[i] and nj = sorted[j] there is no path from nj to ni if j > i .作为拓扑排序节点数组的任何sorted数组都具有以下属性:对于任何节点对ni = sorted[i]nj = sorted[j] ,如果j > i ,则没有从njni的路径。 A directed graph with no cycles is guaranteed to have at least one topological sort of nodes.一个没有环的有向图保证至少有一种拓扑类型的节点。

I have used Kuhn's algorithm (described at the above link) to produce the topological sort given by the following array:我使用了库恩算法(在上面的链接中描述)来生成由以下数组给出的拓扑排序:

[:B, :A, :C, :E, :F, :D, :G, :I, :H]

As shown below, if these nodes are viewed as being on a line, all arcs are directed from left to right.如下图所示,如果将这些节点视为在一条线上,则所有弧都是从左到右指向的。 (For now disregard the numbers shown above the nodes.) (现在忽略节点上方显示的数字。)

在此处输入图像描述

My implementation of the Kuhn algorithm is as follows.我对库恩算法的实现如下。

nodes = graph.keys
  #=> [:A, :B, :C, :D, :E, :F, :G, :H, :I] 
incoming = graph.each_with_object(nodes.map { |n| [n, []] }.to_h) do |(k,v),h|
  v.each { |n| h[n] << k }
end
  #=> {:A=>[], :B=>[], :C=>[:A], :D=>[:A, :B, :C, :F], :E=>[:C], :F=>[:C, :E],
  #    :G=>[:D, :F], :H=>[:E, :F, :G], :I=>[:F, :G]}

incoming[:H] #=> [:E, :F, :G] , for example, shows that the arcs directed into node :H are :E->:H , :F->:H and :G->:H . incoming[:H] #=> [:E, :F, :G]例如,表明指向节点:H的弧是:E->:H:F->:H:G->:H

no_incoming_nodes = incoming.select { |k,v| v.empty? }.keys
  #=> [:A, :B]  
sorted_nodes = []
until no_incoming_nodes.empty?
  n = no_incoming_nodes.pop
  sorted_nodes << n
  graph[n].each do |next_node|
    incoming[next_node].delete(n)
    no_incoming_nodes << next_node if incoming[next_node].empty?
  end
end
sorted_nodes
  #=> [:B, :A, :C, :E, :F, :D, :G, :I, :H]

The second step is to implement a dynamic-programming algorithm to count the number of paths from node :A to node :H .第二步是实现一个动态规划算法来计算从节点:A到节点:H的路径数。 I will explain how it works by explaining the meaning of the numbers above each node in the diagram immediatally above.我将通过解释上图中每个节点上方数字的含义来解释它是如何工作的。

The number of paths from node I (the element of sorted_nodes that is followed by the terminus) is 0 (the number above I) because I is not the terminus and has no outgoing nodes.来自节点 I ( sorted_nodes的元素后面是终点)的路径数为0 (I 上方的数字),因为I不是终点并且没有传出节点。

Going back one node in sorted_nodes , the number of paths from G to the terminus is 1 as it is followed by the terminus ( 1 ) and node I, which has 0 paths to the terminus.返回sorted_nodes中的一个节点,从 G 到终点的路径数为1 ,因为它后面是终点 ( 1 ) 和节点 I,它有0条到终点的路径。

The number of paths from node D to the terminus is 1 because D is followed by only one node, G, and G has 1 path to the terminus.从节点 D 到终点的路径数为1 ,因为 D 后面只有一个节点 G,而 G 有1条到终点的路径。

Node F has 3 paths to the terminus, 1 that goes directly to the terminus, 1 that passes through D, 0 that pass through I and 1 that passes through G.节点 F 有3条路径到终点, 1条直接到达终点, 1条经过 D, 0条经过 I, 1条经过 G。

Similarly, there are 4 paths from node E to the terminus, 8 paths from node C to the terminus and, our objective, 9 paths from A, the origin, to the terminus.同样,从节点 E 到终点有4条路径,从节点 C 到终点有 8 条路径,我们的目标是从起点 A 到终点有9条路径。 The computation can be implemented as follows (using sorted_nodes computed earlier).计算可以如下实现(使用之前计算的sorted_nodes )。

origin   = :A
terminus = :H

tot_to = graph.each_with_object({}) do |(k,v),h|  
  (h[k] = k == terminus ? 1 : 0) if v.empty?
end
  #=> {:H=>1, :I=>0}

(sorted_nodes.index(terminus) - 1).downto(sorted_nodes.index(origin)).each do |i|
  n = sorted_nodes[i]
  tot_to[n] = graph[n].sum { |m| tot_to[m] }
end
tot_to[origin]
  #=> 9

Lastly, I would like to mention that the dynamic programming algorithm could have been organised differently, with roughly equal computational efficiency.最后,我想提一下,动态规划算法可以以不同的方式组织,具有大致相同的计算效率。 Rather than beginning at the terminus and working backward, we could have started at the origin and worked forward until the terminus is reached, at each node computing the number of paths from A to the given node.与其从终点开始并向后工作,我们可以从原点开始并向前工作直到到达终点,在每个节点计算从 A到给定节点的路径数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Ruby - 从给定起点查找通过图形的所有路径 - Ruby - finding all paths through a graph from a given starting point 如何使用Dijkstra算法找到图中从顶点到另一个的最短路径 - How to find the shortest path from a vertex to other in a graph with a Dijkstra's algorithm 将DFS(深度优先搜索)算法应用于邻接矩阵是否是列出图的所有根到叶路径的理想解决方案? - Is DFS (Depth-first search) algorithm applied on an adjacency matrix the ideal solution to list all root-to-leaf paths of a graph? 在Rails 3中,是否可以在视图中从另一个模型调用@variable? - In Rails 3 is it possible or proper to call @variable from another model in a view? 给定字符串的所有可能排列? - All possible permutations of a given String? 查找从4x4阵列的左上角到右下角的所有可能路径 - Find all possible paths from top-left to bottom-right corner of 4x4 array 是否有一种算法可以从给定的大于 1 的加数范围中找到一个和的所有加数组合? - Is there an algorithm to find all the combinations of addends for a sum, from a given range of addends which are greater than 1? 如何获得尽可能少的石墨图计数 - how to get least possible count on graphite graph 这种排序算法的名称是什么? - What's the name of this sorting algorithm? 获取所有可能的子字符串及其计数 - Get all possible substrings and their count
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM