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

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

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

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

В основе этого алгоритма лежит предварительная обработка — построение коренного DAG-графа исходного орграфа (см. лемму 19.2). Алгоритм эффективен, если размер коренного DAG-графа меньше размеров исходного орграфа. Если исходный граф сам является DAG-графом (и поэтому идентичен коренному DAG) или если он содержит лишь несколько небольших циклов, экономия будет незначительной. Но если орграф содержит большие циклы или большие сильные компоненты (т.е. небольшой коренной DAG), то можно разработать оптимальные либо почти оптимальные алгоритмы. Для определенности предположим, что коренной DAG достаточно мал, чтобы представить его матрицей смежности, хотя основная идея применима и для больших коренных DAG.

Для реализации абстрактного транзитивного замыкания мы выполним следующую предварительную обработку графа:

  • Найдем его сильные компоненты.
  • Построим его коренной DAG.
  • Вычислим транзитивное замыкание коренного DAG.

Для поиска сильных компонентов можно воспользоваться алгоритмом Косарайю, Тарьяна или Габова; затем выполнить один проход по ребрам для построения коренного DAG (как описано в следующем абзаце); и выполнить поиск в глубину (программа 19.9) для вычисления транзитивного замыкания. После этой предварительной обработки можно непосредственно получать информацию, необходимую для определения достижимости.

При наличии вектора, индексированного именами вершин, с сильными компонентами орграфа построение представления его коренного DAG в виде матрицы смежности не представляет труда. Вершины DAG-графа являются номерами компонентов орграфа. Для каждого ребра s-t исходного графа нужно просто занести в D->adj[sc[s]] [sc[t]] значение 1. Если бы мы воспользовались представлением списками смежности, нам пришлось бы решать проблему одинаковых ребер в коренном DAG, а в матрице смежности повторяющиеся ребра просто соответствуют занесению 1 в элемент матрицы, который уже равен 1. Этот нюанс очень важен, т.к. в рассматриваемом приложении количество одинаковых ребер в принципе может быть огромным (по сравнению с размером коренного DAG).

Лемма 19.17. Пусть s и t — две вершины в орграфе D, а sc(s) и sc(t) — соответствующие им вершины в коренном DAG-графе K орграфа D. Тогда t достижима из s в D тогда и только тогда, когда sc(t) достижима из sc(s) в K.

Этот простой факт следует из определений. В частности, в данной лемме предполагается, что любая вершина достижима сама из себя (все вершины имеют петли). Если две вершины находятся в одном и том же сильном компоненте (sc(s) = sc(t)), то они взаимно достижимы. $\blacksquare$

Для определения, достижима ли вершина t из заданной вершины s, мы воспользуемся тем же способом, что и для построения коренного DAG. С помощью вектора, индексированного именами вершин, который вычисляется алгоритмом определения сильных компонентов, мы получим (за постоянное время) номера компонентов sc(s) и sc(t), которые будем рассматривать как индексы абстрактных вершин (abstract vertex) в коренном DAG. Их использование в качестве индексов вершин для транзитивного замыкания коренного DAG даст искомый результат.

Программа 19.13 воплощает эти идеи в реализации АТД абстрактного транзитивного замыкания. Кроме того, мы используем интерфейс абстрактного транзитивного замыкания и в коренном DAG. Время выполнения этой реализации зависит не только от количества вершин и ребер орграфа, но и от свойств его коренного DAG. Для анализа алгоритма предположим, что коренной DAG представляется матрицей смежности, поскольку мы ожидаем, что коренной DAG является небольшим и, скорее всего, насыщенным.

Лемма 19.18. На запросы относительно абстрактного транзитивного замыкания орграфа можно отвечать за постоянное время, при затратах памяти, пропорциональных V + v2, и времени, пропорциональных E + v2 + vx, на предварительную обработку (вычисление транзитивного замыкания). Здесь v означает количество вершин в коренном DAG, а x — количество поперечных ребер в его лесе DFS.

Доказательство. Непосредственно следует из леммы 19.13. $\blacksquare$

Если орграф сам является DAG-графом, то вычисление сильных компонентов не дает никакой новой информации, а этот алгоритм делает то же, что и программа 19.9; однако в орграфах общего вида, содержащих циклы, этот алгоритм обычно работает гораздо быстрее, чем алгоритм Уоршелла или решение на основе DFS. Например, из леммы 19.18 сразу вытекает следующий результат.

Лемма 19.19. Можно отвечать за постоянное время на запросы относительно транзитивного замыкания любого графа, в коренном DAG которого содержится менее $\sqrt[3]{V}$ вершин, при затратах объема памяти, пропорционального V, и времени, пропорционального E + V, на предварительную обработку.

Доказательство. Достаточно подставить $v< \sqrt[3]{V}$ в лемму 19.18 и учесть, что x < v2. $\blacksquare$

Программа 19.13. Транзитивное замыкание на основе сильных компонентов

В данном классе реализован интерфейс (абстрактного) транзитивного замыкания орграфов путем вычисления сильных компонентов (с помощью, скажем, программы 19.11), коренного DAG и транзитивного замыкания коренного DAG (программой 19.9). Предполагается, что в классе SC имеется общедоступная функция-член ID, которая возвращает индекс сильного компонента (из массива id) для любой заданной вершины. Эти числа представляют собой индексы вершин в коренном DAG. Вершина t орграфа достижима из вершины s тогда и только тогда, когда ID(t) достижима из ID(s) в коренном DAG.

  template <class Graph>
  class TC
    { const Graph &G; DenseGRAPH *K;
      dagTC<DenseGRAPH, Graph> *Ktc;
      SC<Graph> *Gsc;
    public:
      TC(const Graph &G) : G(G)
        { Gsc = new SC<Graph>(G);
          K = new DenseGRAPH(Gsc->count(), true);
          for (int v = 0; v < G.V(); v++)
            { typename Graph::adjIterator A(G, v);
              for (int t = A.beg(); !A.end(); t = A.nxt())
                K->insert(Edge(Gsc->ID(v), Gsc->ID(t)));
            }
          Ktc = new dagTC<DenseGRAPH, Graph>(*K);
        }
    ~TC() { delete K; delete Ktc; delete Gsc; }
    bool reachable(int v, int w)
        { return Ktc->reachable(Gsc->ID(v), Gsc->ID(w)); }
    } ;
      

Можно рассмотреть и другие варианты этих границ. Например, если мы можем выделить объем памяти, пропорциональный E, то можно достичь того же времени, когда коренной DAG содержит до $\sqrt[3]{E}$ вершин. Более того, эти временные границы обычно завышены, поскольку они предполагают, что коренной DAG насыщен поперечными ребрами — конечно, это совсем не обязательно.

Главный ограничивающий фактор применимости этого метода — это размер коренного DAG. Чем больше рассматриваемый орграф похож на DAG-граф (чем больше его коренной DAG), тем труднее вычислить его транзитивное замыкание. Обратите внимание: мы (конечно) не нарушили нижней границы, установленной в лемме 19.9, поскольку для насыщенных DAG-графов алгоритм выполняется за время, пропорциональное V3. Мы просто значительно расширили класс графов, для которых производительность выше, чем в худшем случае. Вообще-то сложно построить модель случайного орграфа для генерации орграфов, на которых рассматриваемый алгоритм работает медленно (см. упражнение 19.142).

В таблица 19.2 приведены результаты эмпирических сравнений; она показывает, что случайные орграфы имеют небольшие коренные графы даже при средней насыщенности и даже в моделях с серьезными ограничениями на расположение ребер. Гарантий для худшего случая нет, но на практике обычно встречаются большие орграфы с малыми коренными DAG. При работе с такими орграфами можно получить эффективную реализацию АТД абстрактного транзитивного замыкания.

Таблица 19.2. Свойства случайных орграфов
E Случайные ребра Случайные из 10 соседей
v e v e
1000 вершин
1000 983 981 916 755
2000 424 621 713 1039
5000 13 13 156 313
10000 1 1 8 17
20000 1 1 1 1
10000 вершин
50000 144 150 1324 150
100000 1 1 61 123
200000 1 1 1 1
Обозначения:
v Количество вершин в коренном DAG
e Количество ребер в коренном DAG

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

Упражнения

19.138. Разработайте версию реализации абстрактного транзитивного замыкания для орграфов с представлением коренного DAG в виде разреженного графа. Основная сложность состоит в устранении из списка одинаковых ребер без излишних затрат времени или памяти (см. упражнение 19.65).

19.139. Приведите коренной DAG, вычисленный программой 19.13, и его транзитивное замыкание для орграфа

3-71-47-80-55-23-82-90-64-92-66-4.

19.140. Преобразуйте реализацию абстрактного транзитивного замыкания на основе вычисления сильных компонентов (программа 19.13) в эффективную программу, которая вычисляет матрицу смежности транзитивного замыкания орграфа, представленного матрицей смежности, используя алгоритм Габова для вычисления сильных компонентов и усовершенствованный алгоритм Уоршелла для вычисления транзитивного замыкания DAG-графа.

19.141. Эмпирически оцените ожидаемый размер коренного DAG для различных видов орграфов (см. упражнения 19.11—19.18).

19.142. Разработайте модель случайных орграфов, которая генерирует орграфы, имеющие коренные DAG большого размера. Генератор должен порождать ребра по одному, но не должен использовать какие-либо структурные свойства полученного графа.

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

Перспективы

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

На протяжении данной главы постоянно упоминалось решение задачи абстрактного транзитивного замыкания, где используется АТД, который после предварительной обработки может определить, существует ли ориентированный путь из одной заданной вершины в другую. Хотя нижняя граница затрат на предварительную обработку в худшем случае значительно больше V2, метод, рассмотренный в разделе 19.7, сводит базовые методы из этой главы к простому решению, которое обеспечивает оптимальную производительность для многих типах орграфов; заметное исключение составляют лишь насыщенные DAG-графы. Нижняя граница означает, что трудно достичь более высокой гарантированной производительности на всех графах, однако для графов, встречающихся на практике, рассмотренные методы позволяют получить неплохую производительность.

Таблица 19.3. Трудоемкость операций обработки орграфов в худшем случае
Задача Затраты Алгоритм
Орграфы
Обнаружение циклов E DFS
Транзитивное замыкание V(E+V) DFS из каждой вершины
Кратчайшие пути из одного источника E DFS
Все кратчайшие пути V(E+V) DFS из каждой вершины
Сильные компоненты E Косарайю, Тарьяна или Габова
Транзитивное замыкание E+v(v+x) Коренной DAG
DAG-графы
Проверка на отсутствие циклов E DFS или очередь истоков
Топологическая сортировка E DFS или очередь истоков
Транзитивное замыкание V(E+V) DFS
Транзитивное замыкание V(E+X) DFS / динамическое программирование

Здесь приведены данные о затратах (время выполнения в худшем случае) на выполнение различных задач обработки орграфов, рассмотренных в этой главе — для случайных графов и графов, в которых ребра случайным образом соединяют каждую вершину с одной из 10 заданных соседних вершин. Все затраты определялись для представления списками смежности. В случае представления матрицей смежности вместо E элементов появляются V2 элементов, и тогда, например, затраты на вычисление всех кратчайших путей пропорциональны V3. Линейные по времени алгоритмы являются оптимальными, поэтому затраты позволяют с достаточной уверенностью предсказывать время выполнения для любых входных данных; оценки для других могут быть слишком осторожными, и время выполнения для некоторых видов графов может оказаться меньшим. Характеристики производительности самого быстрого алгоритма вычисления транзитивного замыкания орграфа зависят от структуры орграфа — в частности, от размера его коренного DAG.

Разработка алгоритмов с характеристиками производительности, аналогичными характеристикам алгоритмов объединения-поиска из "Введение" , для насыщенных орграфов остается призрачной мечтой. В идеальном случае хотелось бы определить АТД, позволяющий добавлять ориентированные ребра или проверять достижимость одной вершины из другой, и разработать реализацию с выполнением всех операций за постоянное время (см. упражнения 19.153—19.155). Как было сказано в "Введение" , можно довольно близко подойти к этой цели в случае неориентированных графов, но аналогичные решения для орграфов или DAG-графов до сих пор не известны. (Учтите, что удаление ребер — сложная задача даже для неориентированных графов.) Эта задача динамической достижимости (dynamic reachability) не только интересует математиков и имеет практическое применение, но и играет исключительно важную роль в разработке алгоритмов на более высоком уровне абстракции. Например, достижимость лежит в основе задачи реализации сетевого симплексного алгоритма для определения потоков с минимальной стоимостью — эту широко применяемую модель решения задач мы рассмотрим в "Потоки в сетях" .

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

Доминаторы. Пусть задан DAG-граф, все вершины которого достижимы из одного истока г. Говорят, что вершина v доминирует (dominate) над вершиной t, если каждый путь из г в t содержит s. (В частности, каждая вершина доминирует сама над собой.) Каждая вершина v, отличная от источника, имеет непосредственный доминатор, который доминирует над вершиной v, но не доминирует над другим доминатором v и над самим собой. Множество непосредственных доминаторов представляет собой дерево, которое охватывает все вершины, достижимые из источника. Эта структура имеет важное значение для построения компиляторов. Доминаторное дерево может быть вычислено за линейное время с помощью подхода на основе DFS, который использует несколько вспомогательных структур данных, хотя обычно на практике применяется несколько более медленная версия.

Транзитивное приведение. Для заданного орграфа требуется найти орграф, который имеет то же транзитивное замыкание и минимальное количество ребер из всех таких орграфов. Эта задача разрешима (см. упражнение 19.150); однако при дополнительном ограничении — чтобы результатом был подграф исходного графа — она является NP-трудной.

Ориентированный евклидов путь. Существует ли в заданном орграфе путь, соединяющий две заданных вершины, который содержит каждое ребро орграфа в точности один раз? Эта задача легко решается с помощью примерно тех же рассуждений, что и соответствующая задача для неориентированных графов, которая была рассмотрена в "Виды графов и их свойства" (см. упражнение 17.92).

Ориентированный маршрут почтальона. В заданном орграфе нужно найти ориентированный цикл с минимальным количеством ребер, который использует каждое ребро графа минимум один раз (каждое ребро можно использовать многократно). Как мы увидим в "Потоки в сетях" , эта задача сводится к задаче поиска потоков минимальной стоимости, и поэтому она разрешима.

Ориентированный гамильтонов путь. Нужно найти в орграфе простой ориентированный путь максимальной длины. Эта задача относится к числу NP-трудных, однако легко решается, если граф представляет собой DAG (см. упражнение 19.114).

Односвязный подграф. Говорят, что граф односвязен (uniconnected), если между любой парой вершин существует не более чем один ориентированный путь. Если задан орграф и целое число к, нужно определить, существует ли односвязный подграф с не менее чем к ребрами. Для произвольного к эта задача является NP-трудной.

Множество вершин обратной связи. Требуется определить, содержит ли заданный орграф подмножество из максимум к вершин, которое содержит не менее одной вершины из каждого направленного цикла в G. Эта задача является NP-трудной.

Четный цикл. Требуется определить, содержит ли заданный орграф цикл четной длины. Как было сказано в "Виды графов и их свойства" , это задача не является трудноразрешимой, однако никому не удалось создать практически полезный алгоритм.

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

Упражнения

19.144. Используйте программы 17.18 и 17.19 для реализации функции АТД, которая выводит эйлеров путь в орграфе, если он существует. Поясните назначение каждого изменения и дополнения, которые понадобится внести в код.

19.145. Начертите дерево доминаторов орграфа

3-71-47-80-55-23-02-90-64-92-6

6-41-58-29-08-34-52-31-63-57-6

19.146. Напишите класс, использующий поиск в глубину для создания представления родительскими ссылками для доминаторного дерева заданного орграфа (см. раздел ссылок).

19.147. Найдите транзитивное приведение орграфа

3-71-47-80-55-23-02-90-64-92-66-41-58-29-08-34-52-31-63-57-6

19.148. Найдите подграф графа

3-71-47-80-55-23-02-90-64-92-6

6-41-58-29-08-34-52-31-63-57-6

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

19.149. Докажите, что у каждого DAG-графа имеется уникальное транзитивное приведение, и напишите эффективную реализацию функции АТД для вычисления транзитивного приведения DAG-графа.

19.150. Напишите эффективную функцию АТД для орграфов, которая вычисляет транзитивное приведение.

19.151. Приведите алгоритм, который определяет, является ли заданный орграф односвязным. Ваш алгоритм должен выполняться в худшем случае за время, пропорциональное VE.

19.152. Найдите наибольший односвязный подграф в орграфе

3-71-47-80-55-23-02-90-64-92-6

6-41-58-29-08-34-52-31-63-57-6

19.153. Напишите класс орграфа, который поддерживает операции вставки ребра, удаления ребра и проверки, находятся ли две вершины в одном и том же сильном компоненте. Операции построения, вставки ребра и удаления должны выполняться за линейное время, а ответы на запросы о сильной связности — за постоянное время.

19.154. Выполните упражнение 19.153 так, чтобы вставка ребра, удаление ребра и запросы относительно сильных компонентов выполнялись за время, пропорциональное log V в худшем случае.

19.155. Выполните упражнение 19.153 так, чтобы вставка ребра, удаление ребра и запросы относительно сильных компонентов выполнялись за почти постоянное время (как в алгоритмах поиска-объединения для определения связности в неориентированных графах).

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

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

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

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

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

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