Опубликован: 05.01.2015 | Доступ: свободный | Студентов: 2177 / 0 | Длительность: 63:16:00
Лекция 19:

Орграфы и DAG-графы

Достижимость и транзитивное замыкание

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

Определение 19.5. Транзитивное замыкание (transitive closure) орграфа — это орграф с теми же вершинами, но в котором ребро из s в t существует тогда и только тогда, когда существует ориентированный путь из s в t в заданном орграфе.

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

 Транзитивное замыкание

Рис. 19.13. Транзитивное замыкание

Исходный орграф (вверху) содержит лишь восемь ориентированных ребер, но его транзитивное замыкание (внизу) показывает, что существуют ориентированные пути, соединяющие 19 из 30 пар вершин. Транзитивное замыкание отражает структурные свойства орграфа. Например, строки 0, 1 и 2 матрицы смежности в транзитивном замыкания идентичны (как и столбцы 0, 1 и 2), поскольку эти вершины содержатся в ориентированном цикле исходного орграфа.

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

Перемножение булевых матриц. Булевой матрицей (Boolean matrix) называется матрица, элементы которой принимают двоичные значения, т.е. 0 или 1. Для заданных булевых матриц A и B можно вычислить произведение C, используя вместо арифметических операций сложения и умножения, соответственно, логические операции И и ИЛИ.

Классический алгоритм вычисления произведения двух матриц размером V х V вычисляет для каждого s и t скалярное произведение строки s первой матрицы и строки t второй матрицы:

  for (s = 0; s < V; s++)
    for (t = 0; t < V; t++)
      for (i = 0, C[s][t] = 0; i < V; i++)
        C[s][t] = A[s][i] * B[i][t];
      

В матричной системе обозначений такая операция записывается просто как C = A * B. Эта операция определена для матриц, состоящих из любых типов элементов, для которых определены операции 0, + и *. В частности, если a + b интерпретируется как логическая операция ИЛИ, а операция a * b — как логическая операция И, то получается умножение булевых матриц. В языке C++ можно воспользоваться таким вариантом:

  for (s = 0; s < V; s++)
    for (t = 0; t < V; t++)
      for (i = 0, C[s][t] = 0; i < V; i++)
        if (A[s][i] && B[i][t]) C[s][t] = 1;
      

Чтобы вычислить элемент C[s][t] произведения, вначале мы обнуляем его, а затем присваиваем ему значение 1, если находится значение i, для которого как A[s][i], так и B[i][t] равны 1. Это эквивалентно занесению в C[s][t] значения 1 тогда и только тогда, когда результат побитовой операции И над строкой s матрицы A и столбцом t матрицы B не равен нулю.

Теперь пусть A — матрица смежности орграфа A, и мы используем приведенный выше код для вычисления $C = A * A \equiv A^{2}$ (только нужно заменить идентификатор B на A). В этом случае для каждой пары вершин s и t матрица C содержит ребро из s в t тогда и только тогда, когда имеется такая вершина i, для которой в A существует как путь из s и i, так и путь из i и t. То есть ориентированные ребра в A2 в точности соответствуют ориентированным путям в A длиной 2. Если учитывать петли в каждой вершине A, то A2 будет содержать ребра из матрицы A, иначе их там не будет. Эта зависимость между умножением булевых матриц и путями в орграфе продемонстрирована на рис. 19.14. Она немедленно приводит к элегантному методу вычисления транзитивного замыкания любого орграфа.

Лемма 19.6. Транзитивное замыкание орграфа можно вычислить, построив матрицу смежности A этого графа, добавив петли для каждой вершины и вычислив AV. Доказательство. Продолжим рассуждения из предыдущего абзаца: A3 содержит ребра для каждого пути орграфа длиной меньше или равной 3, в матрице A4 содержится каждый путь орграфа, длина которого меньше или равна 4, и т.д. Нет необходимости рассматривать пути, длина которых больше V: любой такой путь

хотя бы один раз должен повторно пройти через одну из вершин (поскольку в графе всего V вершин), поэтому он не добавляет какую-то новую информацию в транзитивное замыкание. Ведь оба конца такого пути уже соединены ориентированным путем длиной меньше V (который можно получить, удалив цикл в повторно посещенную вершину). $\blacksquare$

На рис. 19.15 показаны различные степени матрицы смежности для одного и того же орграфа в процессе вычисления транзитивного замыкания. Данный метод выполняет V матричных умножений, каждое из которых выполняется за время, пропорциональное V3, что в конечном итоге составляет V4. Но на самом деле транзитивное замыкание для любого орграфа можно вычислить с помощью лишь $\lceil lgV\rceil$ операций умножения булевых матриц A2, A4, A8, ..., пока показатель степени не станет больше или равен V. Как показано в доказательстве леммы 19.6, At = AV для любого t > V; так что в результате этого вычисления, требующего на свое выполнение времени, пропорционального V3lgV , получится транзитивное замыкание AV.

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

  for (i = 0; i < V; i++)
    for (s = 0; s < V; s++)
      for (t = 0; t < V; t++)
        if (A[s][i] && A[i][t]) A[s][t] = 1;
      

Этот классический метод, предложенный С. Уоршеллом (S. Warshall) в 1962 г., наиболее удобен для вычисления транзитивных замыканий насыщенных орграфов. Данный код похож на код возведения в квадрат булевой матрицы: различие (важное!) заключается в порядке выполнения циклов for.

 Вычисление квадрата матрицы смежности

Рис. 19.14. Вычисление квадрата матрицы смежности

Если обнулить главную диагональ матрицы смежности орграфа, то квадрат такой матрицы будет представлять собой граф с ребрами, соответствующими каждому пути длиной 2 (вверху). Если заполнить главную диагональ единицами, то квадрат такой матрицы будет представлять собой граф с ребрами, соответствующими каждому пути длиной 1 или 2 (внизу).

 Степени матрицы смежности и ориентированные пути

Рис. 19.15. Степени матрицы смежности и ориентированные пути

Здесь показана последовательность из первой, второй, третьей и четвертой степени (справа, сверху вниз) матрицы смежности, изображенной справа вверху. Эта последовательность порождает графы с ребрами для каждого из путей длиной меньше, соответственно, 1, 2, 3 и 4 (слева, сверху вниз) в графе, представленном этой матрицей. Граф в нижней части рисунка представляет собой транзитивное замыкание для этого примера, поскольку в данном случае не существует путей длиной больше 4, которые соединяют вершины, не соединенные более короткими путями.

Лемма 19.7. С помощью алгоритма Уоршелла ( рис. 19.16) можно вычислить транзитивное замыкание орграфа за время, пропорциональное V3.

Доказательство. Оценка времени выполнения непосредственно следует из структуры кода. Докажем, что он вычисляет транзитивное замыкание, методом индукции по i. После первого выполнения тела цикла матрица содержит 1 на пересечении строки s и столбца t тогда и только тогда, когда существуют пути s-t или s-0-t. Вторая итерация проверяет все пути между s и t, которые содержат вершину 1 и, возможно, 0 — такие как s-1-t, s-1-0-t и s-0-1-t. Мы приходим к следующему индуктивному предположению: i-я итерация цикла заносит 1 в ячейку матрицы на пересечении строки s и столбца t тогда и только тогда, когда в орграфе существует ориентированный путь из s в t, который не содержит вершин с индексами, большими i (за исключением, возможно, конечных точек s и t). Как только что было доказано, это условие выполняется, когда i равно 0. Если это условие верно для i-ой итерации цикла, то путь из s в t, который не содержит вершин с индексами больше i + 1, существует тогда и только тогда, когда: (1) существует путь из s в t, который не содержит вершин с индексами, большими i — в этом случае в A[s][t] занесено 1 на предыдущей итерации цикла (в соответствии с индуктивным предположением); либо (2) существует путь из s в i+1 и путь из i+1 в t, и ни один из них не содержит вершин с индексами, большими i (за исключением конечных точек) — в этом случае в A[s][i + 1] и A[i+1][t] были ранее записаны 1 (по индуктивному предположению). Следовательно, внутренний цикл заносит 1 в A[s][t].$\blacksquare$

 Алгоритм Уоршелла

Рис. 19.16. Алгоритм Уоршелла

Здесь показан процесс формирования транзитивного замыкания (внизу) для простого орграфа (вверху) с помощью алгоритма Уоршелла. Первая итерация цикла (левый столбец, вверху) добавляет ребра 1-2 и 1-5, поскольку существуют пути 1-0-2 и 1-0-5, которые содержат вершину 0 (но не содержат вершины с большими номерами). Вторая итерация (левый столбец, вторая сверху) добавляет ребра 2-0 и 2-5, поскольку существуют пути 2-1-0 и 2-1-0-5, которые содержат вершину 1 (но не содержат вершины с большими номерами). Третья итерация (левый столбец, внизу) добавляет ребра 0-1, 3-0, 3-1 и 3-5, поскольку существуют пути 0-2-1, 3-2-1-0, 3-2-1 и 3-2-1-0-5, которые содержат вершину 2 (но не содержат вершины с большими номерами). В правом столбце показаны ребра, добавленные при просмотре путей через вершины 3, 4 и 5. Последняя итерация (правый столбец, внизу) добавляет в вершину 4 ребра из вершин 0, 1 и 2, т.к. все ориентированные пути из этих вершин в вершину 4 содержат 5 — вершину с наибольшим номером.

Производительность алгоритма Уоршелла можно повысить с помощью простого преобразования кода: проверку элемента A[s][i] можно вынести из внутреннего цикла, поскольку при изменении t его значение не меняется. Это позволит вообще не выполнять t-й цикл, когда A[s][i] равен нулю. Достигаемая экономия зависит от конкретного орграфа и зачастую весьма существенна (см. упражнения 19.53 и 19.54). Программа 19.3 содержит это усовершенствование и позволяет клиентам сначала выполнить предварительную обработку орграфа (вычислить транзитивное замыкание), а затем получать за постоянное время ответ на любой запрос о достижимости.

Бактыгуль Асаинова
Бактыгуль Асаинова

Здравствуйте прошла курсы на тему Алгоритмы С++. Но не пришел сертификат и не доступен.Где и как можно его скаачат?

Александра Боброва
Александра Боброва

Я прошла все лекции на 100%.

Но в https://www.intuit.ru/intuituser/study/diplomas ничего нет.

Что делать? Как получить сертификат?