Приведу пример цикла в ориентированном графе - PullRequest
5 голосов
/ 16 марта 2012

Мне нужен алгоритм, который дает один экземпляр цикла в ориентированном графе, если он есть.Кто-нибудь может показать мне направление?В псевдокоде или, предпочтительно, в Ruby?

Ранее я задавал аналогичный вопрос , и, следуя предложенным там рекомендациям, я реализовал алгоритм Кана в Ruby, который определяет, есть ли в цикле граф, но я хочу не только иметь ли он цикл, но и один возможный случай такого цикла.

example_graph = [[1, 2], [2, 3], [3, 4], [3, 5], [3, 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, 3], [3, 6], [6, 2]]

Если бы я вывел переменную graph в приведенном выше коде в конце экзамена он выдаст:

#=> [[2, 3], [3, 4], [3, 5], [3, 6], [6, 2]]

, который включает нужный мне цикл, но также включает дополнительные ребра, которые не имеют отношения к циклу.

Ответы [ 2 ]

5 голосов
/ 16 марта 2012

Я задал тот же вопрос на сайте обмена математическими стеками и получил ответ. Оказалось, что алгоритм Тарьяна хорош для решения этой проблемы. Я реализовал это в 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], [2, 3], [3, 4], [3, 5], [3, 6], [6, 2]]
DirectedGraph.strongly_connected_components(example_graph)
#=> [[4], [5], [2, 3, 6], [1]]

Выбрав те компоненты, которые длиннее одного, я получаю циклы:

DirectedGraph.strongly_connected_components(example_graph)
.select{|a| a.length > 1}
#=> [[2, 3, 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, 3], [3, 6], [6, 2]]]
2 голосов
/ 16 марта 2012

Первый поиск по глубине, где вы отслеживаете посещенные вершины, а родительский цикл даст вам цикл. Если вы видите ребро ранее посещенной вершины, вы обнаружили цикл между вашим родителем, вами и этой вершиной. Небольшая проблема, с которой вы можете столкнуться, состоит в том, что, если это цикл длиной> 3, вы сможете определить только три вовлеченные вершины и должны будете провести некоторое исследование, чтобы найти остальные вершины в цикле.

Для исследования вы можете начать поиск в ширину, сначала «вверх» по дереву, начиная с родительского и ища посещенную вершину, вы сможете найти весь цикл, выполнив это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...