Алгоритмы для идентификации всех оснований цикла в неориентированном графе

Муравей является главным образом инструментом сборки.

Знаток является проектом и инструментом управления зависимостей (который, конечно, разрабатывает Ваш проект также).

Муравей + Ivy является довольно хорошей комбинацией, если Вы хотите избежать Знатока.

10
задан Graviton 25 April 2019 в 06:44
поделиться

4 ответа

Из того, что я может сказать, не только догадка Брайана, но и еще более сильное утверждение: каждый край, который ' s не в минимальном остовном дереве добавляет ровно один новый «базовый цикл».

Чтобы увидеть это, давайте посмотрим, что происходит, когда вы добавляете ребро E, которого нет в MST. Давайте сделаем излюбленный математический способ усложнить ситуацию и добавить некоторые обозначения;) Назовем исходный граф G, график до добавления E G 'и график после добавления E G' '. Итак, нам нужно выяснить, как «счетчик базовых циклов» меняется с G 'на G'.

Добавление E должно закрывать по крайней мере один цикл (иначе E будет в MST G в первую очередь). Очевидно, что он должен добавить по крайней мере один «базовый цикл» к уже существующим в G '. Но добавляет ли он больше одного?

Он не может сложить более двух, поскольку ни одно ребро не может быть членом более двух базовых циклов. Но если E является членом двух базовых циклов, то «объединение» из этих двух базовых циклов должен был быть базовый цикл в G ', поэтому мы снова получаем, что изменение количества циклов по-прежнему равно 1.

Таким образом, для каждого ребра, не входящего в MST, вы получаете новый базовый цикл. Итак, «подсчет» прост. Найти все ребра для каждого базового цикла немного сложнее, но, следуя приведенным выше рассуждениям, я думаю, что это можно сделать (в псевдо-Python):

for v in vertices[G]:
    cycles[v] = []

for e in (edges[G] \ mst[G]):
    cycle_to_split = intersect(cycles[e.node1], cycles[e.node2])
    if cycle_to_split == None:
        # we're adding a completely new cycle
        path = find_path(e.node1, e.node2, mst[G])
        for vertex on path:
            cycles[vertex].append(path + [e]) 
        cycles
    else:
        # we're splitting an existing base cycle
        cycle1, cycle2 = split(cycle_to_split, e)
        for vertex on cycle_to_split:
            cycles[vertex].remove(cycle_to_split)
            if vertex on cycle1:
                cycles[vertex].append(cycle1)
            if vertex on cycle2:
                cycles[vertex].append(cycle2)

base_cycles = set(cycles)

Изменить : код должен найти все базовые циклы в график (base_cycles установлен внизу). Предполагается, что вы знаете, как:

  • найти минимальное остовное дерево графа (mst [G])
  • найти разницу между двумя списками (ребра \ mst [G])
  • найти пересечение двух списков
  • найти путь между две вершины на MST
  • разбивают цикл на два, добавляя к нему дополнительное ребро (функция разделения)

И это в основном следует из обсуждения выше. Для каждого ребра, не входящего в MST, у вас есть два случая: либо он создает полностью новый базовый цикл, либо разделяет существующий на две части. Чтобы отследить, какой из двух случаев является случаем, мы отслеживаем все базовые циклы, частью которых является вершина (используя словарь циклов).

6
ответ дан 4 December 2019 в 00:25
поделиться

я бы начал с рассмотрения любого алгоритма минимального связующего дерева (Prim, Kruskal и т. Д.). Базовых циклов не может быть больше (если я правильно понимаю), чем ребер, которых НЕТ в MST ....

4
ответ дан 4 December 2019 в 00:25
поделиться

Стандартный способ обнаружения цикла - использовать два итератора - для каждой итерации один перемещает вперед на один шаг, а другие два. Если будет цикл, они в какой-то момент будут указывать друг на друга.

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

0
ответ дан 4 December 2019 в 00:25
поделиться

Ниже приведен мой реальный непроверенный код C # для поиска всех этих «базовых циклов»:

public HashSet<List<EdgeT>> FindBaseCycles(ICollection<VertexT> connectedComponent)
{
   Dictionary<VertexT, HashSet<List<EdgeT>>> cycles =
       new Dictionary<VertexT, HashSet<List<EdgeT>>>();

   // For each vertex, initialize the dictionary with empty sets of lists of
   // edges
   foreach (VertexT vertex in connectedComponent)
       cycles.Add(vertex, new HashSet<List<EdgeT>>());

   HashSet<EdgeT> spanningTree = FindSpanningTree(connectedComponent);

   foreach (EdgeT edgeNotInMST in
           GetIncidentEdges(connectedComponent).Except(spanningTree)) {
       // Find one cycle to split, the HashSet resulted from the intersection
       // operation will contain just one cycle
       HashSet<List<EdgeT>> cycleToSplitSet =
           cycles[(VertexT)edgeNotInMST.StartPoint]
               .Intersect(cycles[(VertexT)edgeNotInMST.EndPoint]);

       if (cycleToSplitSet.Count == 0) {
           // Find the path between the current edge not in ST enpoints using
           // the spanning tree itself
           List<EdgeT> path =
               FindPath(
                   (VertexT)edgeNotInMST.StartPoint,
                   (VertexT)edgeNotInMST.EndPoint,
                   spanningTree);

           // The found path plus the current edge becomes a cycle
           path.Add(edgeNotInMST);

           foreach (VertexT vertexInCycle in VerticesInPathSet(path))
               cycles[vertexInCycle].Add(path);
       } else {
            // Get the cycle to split from the set produced before
            List<EdgeT> cycleToSplit = cycleToSplitSet.GetEnumerator().Current;
            List<EdgeT> cycle1 = new List<EdgeT>();
            List<EdgeT> cycle2 = new List<EdgeT>();
            SplitCycle(cycleToSplit, edgeNotInMST, cycle1, cycle2);

            // Remove the cycle that has been splitted from the vertices in the
            // same cicle and add the results from the split operation to them 
            foreach (VertexT vertex in VerticesInPathSet(cycleToSplit)) {
                cycles[vertex].Remove(cycleToSplit);
                if (VerticesInPathSet(cycle1).Contains(vertex))
                     cycles[vertex].Add(cycle1);
                     if (VerticesInPathSet(cycle2).Contains(vertex))
                         cycles[vertex].Add(cycle2); ;
            }
       }
   }
   HashSet<List<EdgeT>> ret = new HashSet<List<EdgeT>>();
   // Create the set of cycles, in each vertex should be remained only one
   // incident cycle
       foreach (HashSet<List<EdgeT>> remainingCycle in cycles.Values)
           ret.AddAll(remainingCycle);

       return ret;
}

Код Огги был очень хорош и clear , но я почти уверен, что он содержит ошибку, или это я не понимаю ваш псевдокод Python :)

cycles[v] = []

не может быть индексированным по вершинам словарем списков ребер. На мой взгляд, это должен быть индексированный по вершинам словарь наборов списков ребер.

И, чтобы добавить уточнение:

for vertex on cycle_to_split:

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

Повторяю, это непроверенный и неполный код, но это шаг вперед. Он по-прежнему требует правильной структуры графа (я использую список инцидентов) и многих алгоритмов графов, которые вы можете найти в учебниках, таких как Кормен. Мне не удалось найти FindPath () и SplitCycle () в учебниках, и их очень сложно закодировать за линейное время количества ребер + вершин в графе. Сообщу о них здесь, когда я их протестирую.

Большое спасибо, Огги!

2
ответ дан 4 December 2019 в 00:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: