Опубликован: 06.10.2011 | Доступ: свободный | Студентов: 1678 / 101 | Оценка: 4.67 / 3.67 | Длительность: 18:18:00
Лекция 11:

Проектирование и инженерия алгоритма: топологическая сортировка

Тотальный порядок

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

Определение: отношение тотального порядка (строгое)
Тотальным порядком является отношение порядка, обладающее дополнительным свойством.
О4 Для любых a и b имеет место одно из следующих свойств:
  • [a, b] принадлежит r;
  • [b, a] принадлежит r;
  • a = b.

Для понимания О4 заметьте: из асимметрии (О3) следует, что только одна из первых двух возможностей может существовать. Из антирефлексивности (О1) следует, что последняя возможность исключает две первые. Так что только одна из трех возможностей может существовать. Новое условие добавляет, что одна из трех возможностей должна существовать.

Отношение "<" на целых является тотальным отношением. Но не каждое отношение порядка является таковым. Отношение "«" на точках плоскости таковым не является, так как это означало бы, что для любых двух различных точек плоскости p1 и p2 должно выполняться p1 « p2 или p2 « p1.

Точки на плоскости и отношение полного порядка

Рис. 10.7. Точки на плоскости и отношение полного порядка

Заметьте, пара точек [a, c] не принадлежит отношению, так как не верно, что a « c; точно так же неверно, что и c « a. Другим контрпримером является пара точек [b, c].

На множестве четырех точек можно установить различные отношения порядка, являющиеся полными. В частности, любое перечисление, в котором каждая точка появляется ровно один раз, можно рассматривать как задание полного порядка t. Вот как можно определить этот порядок: пара точек [p, q] принадлежит t, если и только если p предшествует q в перечислении. Например, перечисление [a, b, c, d] определяет полный порядок:

{[a, b], [a, c], [a, d],                 [1]
    [b, c], [b, d],
                    [c, d]}
        
Пример 10.1.

Верно и обратное: каждый полный порядок определяет единственную нумерацию.

Такой тотальный порядок является топологической сортировкой исходного отношения "« ", если и только если тотальный порядок совместим с исходным. Совместимость означает, что когда p « q в исходном порядке, то p предшествует q в тотальном порядке.

Точки на плоскости и отношение «

Рис. 10.8. Точки на плоскости и отношение «

Мы уже видели, что три тотальных порядка удовлетворяют этому требованию для нашего примера: соответствующими перечислениями являются a, b, c, dпримере 10.1 представлено в виде множества пар); a, c, b, d и c, a, b, d.

Что означает "совместимость", если это понятие рассматривать более точно? Его нетрудно специфицировать, благодаря определению отношения как множества пар. Сказать, что тотальный порядок, заданный перечислением a, b, c, d, совместим с заданным ациклическим отношением, – это все равно, что сказать, что множество пар этого отношения является подмножеством множества пар тотального отношения. Каждая пара в отношении порядка является парой в тотальном отношении порядка. В нашем примере отношение « является множеством пар:

{[a, b], [a, d], [b, d], [c, d]}             [2]
        
Пример 10.2.

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

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

Ациклические отношения имеют топологическую сортировку.

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

Справедлива следующая теорема.

Теорема о топологической сортировке
Для любого ациклического отношения r над конечным множеством А существует тотальное отношение порядка t над А, такое, что r\subseteq t

Для доказательства этой теоремы можно было бы использовать то наблюдение, что r\subseteq r^+, где r^+ – это транзитивное замыкание r, и предыдущее доказательство того, что r^+ является отношением порядка. Этого достаточно, чтобы расширить r^+ до полного отношения порядка. Но для наших целей более интересно другое доказательство. Это конструктивное доказательство (основанное на теореме об отсутствии предшественника), позволяющее нам непосредственно получить схему алгоритма.

Доказательство ведется индукцией по числу элементов n множества A. Если n = 0, множество пусто, единственно возможным отношением является пустое отношение (пустое множество пар элементов из A), которое является полным порядком. Это доказывает базис индукции.

Если вы предпочитаете иное, в качестве базиса можно выбрать случай n = 1, для которого A состоит из одного элемента x. Хотя теперь A не пусто, единственное ациклическое отношение в A снова является пустым отношением, так как единственная пара [x, x], которую можно создать, образует цикл.

Для индукционного шага предположим, что теорема выполняется над множествами из n элементов, и рассмотрим ациклическое отношение на множествах из n + 1 элементов. Рисунок поясняет идею доказательства:
Расширение, дополняющее топологическую сортировку

Рис. 10.9. Расширение, дополняющее топологическую сортировку

Теорема об отсутствии предшественника говорит нам, что А имеет по крайней мере один элемент, не имеющий предшественника. Пусть x – такой элемент. Пусть А' – множество элементов А за исключением x. Пусть r' – отношение на А', содержащее все пары из r, за исключением тех, что содержат x. Очевидно, что r' является ациклическим отношением на A'. По предположению индукции, так как A' имеет n элементов, существует полный порядок t' над A', совместимый с r' (это то же самое, что сказать r^\prime\subseteq t^\prime). Теперь рассмотрим отношение t над A, состоящее из следующих пар:

  • все пары в t';
  • все пары вида [x, y], где y – элемент из A'.
Если вы предпочитаете думать о полном порядке как о перечислении, то можно рассматривать t как перечисление элементов A, которое начинается с x и продолжается перечислением A', заданным t'.

Нетрудно видеть, что t – это полный порядок, и что r\subseteq t; это дает нам полный порядок, совместимый с r, что и доказывает теорему.

Теорема о топологической сортировке является математическим обоснованием программы, которую мы собираемся построить. Более того, доказательство непосредственно приводит к основной идее алгоритма.

10.3. Практические соображения

С теоретическими основами все ясно, так что можно приступать к поиску инженерного решения. Ядром является алгоритм топологической сортировки, но прежде следует проанализировать ограничения производительности и определить рамки инженерии программы.

Требования производительности

Что можно сказать об ожидаемой емкостной и временной сложности алгоритма?

Входом для алгоритма является множество элементов и множество ограничений. Обозначим через n число элементов, m – число ограничений.

Алгоритм должен (в случае ациклического отношения) выполнить:

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

Так что наилучшее время, на которое можно надеяться, – это O(n + m).

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

Каркас класса

Чисто алгоритмическое решение могло бы использовать функцию в форме:

topologically_sorted (elements:…; constraints:…): LIST[…]
    — Перечисление элементов множества elements
    — в порядке, совместимом с множеством ограничений constraints.
        

Предполагается, что входные типы позволяют задать множества элементов и ограничений – elements и costraints, соответствующие нашим прежним обозначениям A и r.

Лучшее решение – как покажет дальнейшая разработка – дает ОО-подход с построением класса TOPOLOGICAL_SORTED, любой экземпляр которого представляет задачу топологической сортировки. Структуры данных, представляющие элементы и ограничения, будут атрибутами класса, формируемые процедурами инициализации, такими как record_element и record_constraint.

Вместо функции topologically_sorted, как выше, будем иметь в классе:

  • процедуру process, выполняющую процесс топологической сортировки;
  • запрос sorted, который возвращает список элементов, созданный при работе процедуры process.

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

Вход и выход

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

Map Louvre
Map Orsay
Pass Louvre
Pass Orsay
Money Pass
        
Элементы и ограничения

Рис. 10.10. Элементы и ограничения

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

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

Для обеспечения общности сделаем наш базовый класс универсальным: TOPOLOGICAL_SORTED[G], где параметр G задает тип элементов. Тогда результат запроса sorted, который обозначает топологически отсортированный список элементов, созданный в process, имеет тип LIST[G]. Две процедуры инициализации, о которых говорилось выше, имеют сигнатуры:

record_element (e: G)
record_constraint (e, f: G)
        

Полная форма алгоритма

Рассмотрим ациклическое отношение r на множестве элементов A. Так как нам необходимы имена, используемые в программе, то позвольте полагать, не предрешая выбор реализации, что класс TOPOLOGICAL_SORTED имеет доступные запросы elements и constraints. Общая схема для алгоритма топологической сортировки в процедуре process такова:

from …until elements.is_empty loop
    "Пусть x – элемент без предшественников в ограничениях"
    "Рассматривать x как следующий элемент sorted"
    "Удалить x из множества элементов"
    "Удалить все пары, начинающиеся x, из множества ограничений)"
end
        

Уточненная нужным образом эта форма будет работать при условии, что мы начинаем с ациклического отношения (или в специальном случае – с отношения порядка).

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

Теоремы о топологической сортировке и об отсутствии предшественника дают нам обоснование решения, но его еще нужно выразить в идее инварианта цикла и варианта (надеюсь, цикл без инварианта – для вас ужасная картина, не правда ли?)

process
        — Выполняет в sorted перечисление элементов множества elements
        — в порядке, совместимом с множеством ограничений constraints.
    require
            — "constraints описывают ациклическое отношение"
    do
        from
            create {….} sorted. make
        invariant
            — "constraints описывают ациклическое отношение"
        until
            elements. is_empty
        loop — Как и ранее, за тем исключением, что явно используется результат sorted:
            "Пусть x – элемент без предшественников в ограничениях"
            sorted. extend (x)
            "Удалить x из множества элементов"
            "Удалить все пары, начинающиеся x, из множества ограничений"
        variant
            elements. count
        end
        ensure
        — "sorted представляет топологическую сортировку elements, согласованную
        — с constraints"
    end