В этой статье рассмотрим одну из важнейших задач структурного анализа — топологическую декомпозицию структуры системы, то есть выделение в ней сильно связных подграфов (подсистем). Будет описан алгоритм и приведена его реализация на языке программирования C#.
Теоретические сведения
Введем некоторые определения. Множество вершин графа G(V, E), достижимых из вершины i, называется достижимым множеством R(i) и определяется следующим образом:
где G(i) — множество правых инциденций вершины i, т. е. множество вершин, достижимых из вершины i с использованием путей длиной, равной единице; G(P)(i) — множество вершин, достижимых из вершины i с использованием путей длиной p. При этом предполагается, что сама вершина i достижима с использованием пути длиной, равной нулю, и включена во множество R(i).
Контрдостижимым множеством Q(i) графа G(V, E) называется множество таких вершин, когда из любой вершины этого множества можно достигнуть вершины i. Оно определяется следующим образом:
где G(-1)(i) — множество левых инциденций вершины i, т. е. множество вершин, из которых можно достичь i-ую вершину, при этом длина пути равна 1 и т. д.
Пересечение множеств R(i) ∩ Q(j) — это множество таких вершин, каждая из которых принадлежит по крайней мере одному пути, идущему от i-ой вершины к j-ой. Эти вершины называются существенными или неотъемлемыми, относительно двух концевых вершин i и j.
Множество R(i) ∩ Q(i) — однозначно определяет сильно связный подграф графа G(V, E), содержащий вершину i, т.к. все вершины этого множества достижимы из i-ой вершины и из каждой вершины достижима вершина i, то есть они взаимно достижимы.
Описание алгоритма
- Выбираем вершину с наименьшим номером i.
- Определяем для нее достижимое множество вершин R(i).
- Определяем для нее контрдостижимое множество вершин Q(i).
- Выделяем в системе сильно связный подграф, включающий множество вершин V = R(i) ∩ Q(i).
- Удаляем все вершины V из первоначального графа.
- ЕСЛИ начальный граф не пуст, ТО переходим к шагу 1. ИНАЧЕ конец.
Пример использования алгоритма
Пусть имеется такой граф:
- Определяем достижимое множество для вершины 1: R(1) = (1, 2, 5, 7, 6, 8, 9, 10).
- Определяем контрдостижимое множество для вершины 1: Q(1) = (1, 9, 8, 10, 3, 6, 4, 7, 2).
- Находим пересечение этих множеств, которое и определяет сильно связный подграф (подсистему), включающий вершину 1: V1 = R(1) ∩ Q(1) => G1(V1, E1) = (1, 2, 6, 7, 8, 9, 10).
- Удаляем все вершины V1 из первоначального графа.
- Поступаем аналогично с остальными вершинами:
V2 = R(3) ∩ Q(3) => G2(V2, E2) = (3),
V3 = R(4) ∩ Q(4) => G3(V3, E3) = (4),
V4 = R(5) ∩ Q(5) => G4(V4, E4) = (5).
В результате декомпозиции получили 4 связных подграфа (подсистемы): G1 (1, 2, 6, 7, 8, 9, 10), G2 (3), G3 (4), G4 (5).
Реализация алгоритма
Рассмотрим реализацию алгоритма топологической декомпозиции на языке программирования C#.
Пусть дуга графа представлено экземпляром класса Edge:
1 2 3 4 5 6 7 8 9 10 |
public class Edge { public int v1, v2; public Edge(int v1, int v2) { this.v1 = v1; this.v2 = v2; } } |
, где v1 – номер вершины, из которой дуга исходит, а v2 — номер вершины, в которую данная дуга заходит.
Подграф представим в виде экземпляра класса Subgraph:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class Subgraph { public List<int> V; public Subgraph() { V = new List<int>(); } public Subgraph(List<int> V) { this.V = new List<int>(V); } } |
, где List<int> V — список номеров вершин, входящих в подграф.
Все множества дуг и подграфов будем хранить в виде списков:
1 2 |
List<Edge> E; List<Subgraph> Subgr = new List<Subgraph>(); |
Приведем реализацию метода topologicalDecomposition(…), выполняющего топологическую декомпозицию структуры.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
public void topologicalDecomposition(int countV, List<Edge> E, List<Subgraph> Sub) { List<int> notUsedV = new List<int>(); //список еще не использованных вершин for (int i = 0; i < countV; i++) notUsedV.Add(i); while (notUsedV.Count > 0) { List<int> R = new List<int>(); //достижимое множество R.Add(notUsedV[0]); List<int> Q = new List<int>(); //контрдостижимое множество Q.Add(notUsedV[0]); int[] color = new int[countV]; //формируем достижимое и контрдостижимое множества for (int i = 1; i < notUsedV.Count; i++) { for (int k = 0; k < countV; k++) { if (notUsedV.IndexOf(k) != -1) color[k] = 1; else color[k] = 2; } if (DFS(notUsedV[0], notUsedV[i], E, color)) R.Add(notUsedV[i]); for (int k = 0; k < countV; k++) { if (notUsedV.IndexOf(k) != -1) color[k] = 1; else color[k] = 2; } if (DFS(notUsedV[i], notUsedV[0], E, color)) Q.Add(notUsedV[i]); } //пересечение множеств R и Q List<int> intersection = new List<int>(R.Intersect(Q).ToList()); Sub.Add(new Subgraph(intersection)); for (int i = 0; i < intersection.Count; i++) { notUsedV.Remove(intersection[i]); } } } |
Аргументы метода topologicalDecomposition: countV — количество вершин в графе, E — список дуг в графе, Sub — список подграфов.
Для обхода графа и поиска множеств R и Q используется рекурсивный алгоритм «Поиск в глубину» (Depth-first search). Опишем принцип его работы:
Пусть дан граф G = (V, E), где V — множество вершин графа, E — множество его ребер. Предположим, что в начальный момент времени все вершины графа окрашены в белый цвет. Выполним следующие действия:
- Переберем все вершины v ∈ V. ЕСЛИ вершина белая, выполним для нее DFS(v).
Процедура DFS (параметр — вершина u ∈ V)
- Перекрашиваем вершину u в черный цвет.
- Для всякой вершины w, смежной с вершиной u и окрашенной в белый цвет, рекурсивно выполняем процедуру DFS(w).
Вот реализация метода DFS для нашей задачи (белый цвет — это 1, черный — 2):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
bool DFS(int u, int endV, List<Edge> E, int[] color) { color[u] = 2; if (u == endV) { return true; } for (int w = 0; w < E.Count; w++) { if (color[E[w].v2] == 1 && E[w].v1 == u) { if (DFS(E[w].v2, endV, E, color)) return true; color[E[w].v2] = 1; } } return false; } |
Аргументы метода DFS: u — номер начальной вершины, endV — номер конечной вершины, E — список дуг графа, color — цвета вершин. Если вершина endV достижима из вершины u, то метод вернет значение true, иначе false.
Скачать листинги из статьи
Поделиться в соц. сетях: