Найти все множества сильно связанных вершин - PullRequest
0 голосов
/ 05 января 2011

У меня есть связанный список ребер с орграфом, в котором я пытаюсь найти все наборы сильно связанных компонентов.Кто-нибудь может указать мне на алгоритм с хорошим временем наихудшего случая (пример псевдо-или C-кода будет высоко ценится).создавать сильно связанные компоненты, а не вершины.На графике ниже обратите внимание, что есть 2 набора ребер для создания сильносвязанной компоненты, однако для обоих используются только два ребра (a-> b и b-> c).Алгоритм должен иметь возможность создавать наборы {a-> b, b-> c, c-> a} и {a-> b, b-> c, c-> b, b-> a}.

http://img521.imageshack.us/img521/8025/digraph.jpg

Надеюсь, это поможет прояснить мою цель.

EDIT2: У меня есть полуработающая реализация, однако я заметил, что она неработать, если граф, в котором я ищу, также сильно связан.Кто-нибудь знает, как найти SCC в SCC?

Ответы [ 3 ]

3 голосов
/ 05 января 2011

Определение Сильно связанный компонент в Википедии рекомендует три алгоритма. Я бы выбрал Тарьяна как лучшее сочетание эффективности и простоты реализации.

Я взял псевдокод в Википедии и изменил его, чтобы сохранить список всех ГТК. Отсюда следует:

Input: Graph G = (V, E)

List<Set> result = empty
index = 0                                   // DFS node number counter 
S = empty                                   // An empty stack of nodes
for all v in V do
  if (v.index is undefined)                 // Start a DFS at each node
    tarjan(v, result)                       // we haven't visited yet

procedure tarjan(v, result)
  v.index = index                           // Set the depth index for v
  v.lowlink = index                               
  index = index + 1
  S.push(v)                                 // Push v on the stack
  for all (v, v2) in E do                   // Consider successors of v
    if (v2.index is undefined)              // Was successor v' visited?
      tarjan(v2, result)                    // Recurse
      v.lowlink = min(v.lowlink, v2.lowlink)
    else if (v2 is in S)                   // Was successor v' in stack S? 
      v.lowlink = min(v.lowlink, v2.index) // v' is in stack but not the dfs tree
  if (v.lowlink == v.index)                // Is v the root of an SCC?
    set interconnected = empty
    previous = v
    repeat
      v2 = S.pop                                  
      interconnected.add((previous,v2)) // record this edge
      last = previous=v2
    until (v2 == v)
    result.add(interconnected)

Редактировать в ответ на дальнейшие уточнения. Видите ли вы, что алгоритм помещает вершины в стек, а затем выталкивает их снова? Я думаю, что это, вероятно, означает, что вы можете знать, что каждый последовательный элемент стека связан с предыдущим ребром. Я изменил псевдокод выше, чтобы отразить это, (но не пробовал).

1 голос
/ 23 января 2011

Даже после вашего второго редактирования я все еще не уверен, какова ваша цель на самом деле. Этому мешает ваш выбор терминологии. SCC, поскольку большинство людей подразумевают, что они являются максимальными наборами вершин, которые все достижимы друг от друга, и какие пути могут возникать просто не имеет значения.

Кто-нибудь знает способ найти SCC в SCC?

По обычным определениям, в SCC нет такой вещи, как SCC, потому что SCC максимален.

Вы ищете что-то еще: вы начинаете с подключенного графа и хотите найти определенные наборы ребер, которые вы не охарактеризовали. Я сильно подозреваю, что если вы четко охарактеризуете то, что хотите, алгоритм потерпит неудачу.

Мое первое предположение состояло в том, что для данного связного графа вам нужны множества ребер всех возможных комбинаций всех путей, соединяющих каждую пару. Если это так, то простой способ сделать это: для каждой пары найти все пути, затем найти все комбинации и устранить дубликаты. Однако при этом генерируется больше наборов, чем указано в вашем примере, поскольку использование всех ребер, безусловно, возможно.

Вместо этого, кажется, вы хотите, чтобы все минимальные наборы ребер все еще позволяли исходным вершинам оставаться SCC. В этом случае каждый элемент набора мощности исходного набора ребер представляет возможного кандидата, и вы хотите, чтобы все кандидаты были связаны с графом, но без надлежащего подмножества, которое все еще подключено.

Это дает простой алгоритм обхода этой решетки множеств и проверки этого условия. Проверить, подключен ли график, просто, как удалить или добавить ребра. Тем не менее, похожие графы имеют в основном одинаковую структуру, поэтому вы захотите использовать то, что можете из предыдущих задач.

Некоторые ссылки, которые могут помочь:

Особенно с точки зрения связи глобальной информации графа с локальной информацией о ребрах и вершинах.


Ваш начальный граф G имеет множество вершин V и ребер E и является сильно связным. Цель состоит в том, чтобы вывести все «минимальные» наборы ребер E 'так, чтобы удаление любого ребра из E разбивало график на несколько SCC. Мы можем сделать это путем поиска всех возможных наборов ребер.

Прямо это:

for E' in powerset(E):
    if (V, E') strongly connected:
        for e in E':
            if (V, E' - e) strongly connected:
                 try next E' # Not minimal
        add G' = (V, E') to list of minimal subsets.

Однако это невероятно расточительно: очевидно, не связанные графики проверено, и связность каждого графа проверена много раз. Мы можем исключить первое путем поиска возможных ребер в гораздо более хорошем порядке, расположение этих ребер в решетке. каждый у ребра есть «прямые потомки», заданные этим ребром за вычетом одного ребра. Как только мы столкнемся с графиком, который не является SCC, нам не нужно проверять его подмножества, поскольку они также не могут быть SCC. Самый простой способ выразить это как график поиска. Хотя поиск в глубину часто путь, так как он использует меньше памяти, я собираюсь сначала выбрать ширину поиск, потому что в дальнейшем мы будем использовать порядок обхода. (Глубина первый поиск изменяет очередь в стек).

push starting node on queue
while queue not empty:
    pop node from queue
    for c in suitable children, not on queue:
        push c on queue
    deal with node

Часть «не в очереди» имеет решающее значение, иначе каждый узел может быть посещен много раз. Это превращает наш алгоритм в:

push E on queue
while queue not empty:
    pop E' from queue
    if (V, E') strongly connected:
        minimal = true
        for e in E':
            if (V, E' - e) strongly connected:
                push E' - e on queue if not on queue
                minimal = false           
        if minimal:
            add G' = (V, E') to list of minimal subsets.

Теперь он пропускает все наборы ребер, которые не могут быть сильно связаны, потому что суперсеты тоже нет. Недостатком является то, что он может возможно использовать много места. Это все еще, однако, неоднократно проверяет подключение. Однако мы можем кэшировать эту информацию.

check_or_add_cached_connected(E)
push E on queue
while queue not empty:
    pop E' from queue
    connected = cached (E')
    if connected:
        minimal = true
        for e in E':
            child_connected = check_or_add_cache(E' - e)
            if child_connected:
                push E' - e on queue if not on queue
                minimal = false
        if minimal:
            add G' = (V, E') to list of minimal subsets.
    remove E' from cache

Теперь, когда мы начали кеширование, мы можем кешировать еще больше. Если удаление ребра ломает график, оно также ломает любой подграф. Это означает, что мы можем отслеживать «необходимые ребра» на графике и распространять их вниз на любой подграф. И мы можем проверить эти необходимыекрая, чтобы избежать проверки подграфов.

check_or_add_cached_connected(E)
push E on queue
while queue not empty:
    pop E' from queue
    (connected, required_edges) = cached (E')
    if connected:
        minimal = true
        for e in E' and not in required_edges:
            child_connected = check_or_add_cached_connected(E' - e)
            if child_connected:
                push E' - e on queue if not on queue
                minimal = false
            else:
                add e to required_edges
        if minimal:
            add G' = (V, E') to list of minimal subsets.
        else:
            for e in E' and not in required_edges:
                merge_cache(E' - e, required_edges)
    remove E' from cache

Структура кэша и очереди требует некоторой работы, чтобы гарантировать, что используемые операции могут быть выполнены эффективно, но это довольно просто.

1 голос
/ 05 января 2011

Страница википедии для сильно связанных компонентов указывает на три алгоритма, все они достаточно подробно объяснены в википедии для непосредственного перевода в исходный код.Если этой информации недостаточно, вам, вероятно, следует указать, чего именно не хватает.

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