我想要一个算法,如果有的话,在有向图中给出一个循环的实例.谁能告诉我一个方向?在伪代码中,或者最好是在
Ruby中?
我之前问过a similar question,并按照那里的建议,我在Ruby中实现了Kahn的算法,它检测图表是否有一个循环,但我不仅要求它是否有一个循环,而且还要一个这样的循环的可能实例.
example_graph = [[1,2],[2,3],[3,4],5],6],[6,2]]
卡恩的算法
def cyclic? graph ## The set of edges that have not been examined graph = graph.dup n,m = graph.transpose ## The set of nodes that are the supremum in the graph sup = (n - m).uniq while sup_old = sup.pop do sup_old = graph.select{|n,_| n == sup_old} graph -= sup_old sup_old.each {|_,ssup| sup.push(ssup) unless graph.any?{|_,n| n == ssup}} end !graph.empty? end
上面的算法告诉图表是否有一个循环:
cyclic?(example_graph) #=> true
但我不仅要这样,而且要像这样的循环示例:
#=> [[2,2]]
#=> [[2,2]]
解决方法
我在
the math stackexchange site问了同样的问题,得到了答案.事实证明,Tarjan的算法有助于解决这个问题.我在Ruby中实现它如下:
module DirectedGraph; module_function ## Tarjan's algorithm def strongly_connected_components graph @index,@stack,@indice,@lowlink,@scc = 0,[],{},[] @graph = graph @graph.flatten(1).uniq.each{|v| strong_connect(v) unless @indice[v]} @scc end def strong_connect v @indice[v] = @index @lowlink[v] = @index @index += 1 @stack.push(v) @graph.each do |vv,w| next unless vv == v if !@indice[w] strong_connect(w) @lowlink[v] = [@lowlink[v],@lowlink[w]].min elsif @stack.include?(w) @lowlink[v] = [@lowlink[v],@indice[w]].min end end if @lowlink[v] == @indice[v] i = @stack.index(v) @scc.push(@stack[i..-1]) @stack = @stack[0...i] end end end
因此,如果我将其应用于上面的示例,我会得到图表中强连接组件的列表:
example_graph = [[1,2]] DirectedGraph.strongly_connected_components(example_graph) #=> [[4],[5],3,[1]]
通过选择那些长于1的组件,我得到了循环:
DirectedGraph.strongly_connected_components(example_graph) .select{|a| a.length > 1} #=> [[2,6]]
而且如果我从图中选择两个顶点都包含在组件中的边,我得到构成循环的关键边:
DirectedGraph.strongly_connected_components(example_graph) .select{|a| a.length > 1} .map{|a| example_graph.select{|v,w| a.include?(v) and a.include?(w)}} #=> [[[2,2]]]