Романовский Иосиф Владимирович
ЗАДАЧА ШТЕЙНЕРА НА ГРАФАХ И ДИНАМИЧЕСКОЕ ПРОГРАММИРОВАНИЕ
1. ВВЕДЕНИЕ
Задачи оптимизации, которые можно решать с помощью динамического программирования, очень разнообразны. Но иногда возможность приведения оптимизационной задачи к динамическому программированию бывает замаскирована неестественной или слишком громоздкой конструкцией.
Здесь мы рассмотрим задачу именно со слишком громоздкой конструкцией, которая позволяет, однако, решить рассмотренную задачу при не очень больших значениях параметров. Тем не менее, приемлемых алгоритмом значений параметра в некоторых ситуациях может оказаться достаточно. Речь пойдет о так называемой задаче Штейнера на графах. Нам будет естественно начать с классической задачи Штейнера, чтобы объяснить, откуда взялось название. Дальше последует постановка интересующей нас задачи, затем напоминание метода Дейкстры для поиска кратчайших путей в графе, затем некоторые простые модификации этого алгоритма, которые будут нам полезны, а затем уже сам алгоритм.
2. ЗАДАЧА ШТЕЙНЕРА
Исходная задача Якоба Штейнера (1796-1863) имеет очень простую формулировку:
На плоскости задано п точек (можно представлять себе карту и дома на ней). Требуется соединить эти точки ломаны-
ми линиями таким образом, чтобы1 каждая точка была соединена с каждой и чтобы суммарная длина всех проведенных линий была минимальна.
Алгоритма решения этой задачи, то есть метода нахождения точного минимума, не существует до настоящего времени. Хорошо известны достаточные условия: в решение могут входить промежуточные точки, и все соединения должны быть отрезками, соединяющими точки (исходные и промежуточные). В каждой промежуточной точке должны сходиться три отрезка, а в исходных точках не более трех. Угол между отрезками, сходящимися в одной точке не должен быть меньше 120°.
Типичное решение, удовлетворяющее перечисленным условиям, изображено на рисунке 1. Семь точек (черные кружки) соединены сетью, в которой четыре дополнительные точки. В каждой дополнительной точке три отрезка сходятся под углом 120°. Соединение явно не оптимально.
Трудность же заключается в том, что одному и тому же набору точек может отвечать несколько таких решений. На рисунке 2 показано два решения, соответствующих четырем точкам - вершинам прямоугольника. Здесь исходных точек четыре, а промежуточных две. И черная, и серая соединяющая сеть удовлетворяют необходимым условиям оптимальности, но длины у них различны.
В связи с ростом интереса к экстремальным задачам на графах и успехами в решении задач об оптимальных деревьях (ос-
Рисунок 1.
товное дерево минимального веса, дерево кратчайших путей, кратчайшее дерево путей) естественно появились трудно решаемые варианты этих задач, в которых нашли аналогию с задачей Штейнера. Такие задачи стали называть графовыми задачами Штейнера. Мы будем рассматривать здесь одну из таких формулировок.
Пусть задан ориентированный граф с множеством вершин М и множеством дуг N. Каждой дуге и сопоставлен некоторый положительный вес си. Заданы также вершина г0 (будем называть ее начальной вершиной) и непустое множество вершин Мх (которые, конечно, называются конечными). Требуется найти частичный подграф, то есть такие подмножества М'с М, ^с N, что у каждой дуги и е N ее начало ,/Ь(и) и конец ,/е(и) содержатся в МЭтот частичный подграф должен содержать пути из г0 в любую из вершин множества Мх , а сумма весов дуг из N должна быть минимальной.
До того как мы будем рассматривать метод для решения этой задачи, вспом ним кое-что известное (метод Дейк-стры) и приспособим его к нашим потребностям.
3. КРАТЧАЙШИЕ ПУТИ
ОТ ОДНОЙ ДО ОДНОЙ ВЕРШИНЫ
Обычный метод Дейкстры заключается в том, что, как полагается в динамическом программировании, единичная задача нахож- ^ рьрлкяоЛ
Рисунок 2.
дения кратчайшего пути от вершины г0 до вершины решается вместе со всеми аналогичными задачами нахождения кратчайшего пути от г0 до любой другой вершины г е М. Особенностью метода Дейкстры в сравнении с другими методами, основанными на идее динамического программирования, является особый порядок вычислений.
Именно, выделяется множество вершин, расстояние до которых от г0 находится в стадии вычисления. Это множество вершин принято обозначать через Мг Все остальные вершины принадлежат либо множеству М0 вершин, расстояние до которых уже вычислено, либо множеству М2 вершин, расстояние до которых вычисляться не начиналось.
Первоначально М1 = {г0}, М2 = М \ Мр
М0 = 0.
На каждом шаге итеративного процесса в М1 выбирается вершина г1, расстояние до которой минимально, эта вершина а затем она используется для корректировки других расстояний. Именно, путь до г1 всевозможными способами наращивается на еще одну дугу (выходящую из г'1), получившиеся пути естественным образом используются для исправления тех данных о процессе, которые представлены мно-
переводится в М0 /-
крл&гайшие ау&к
коНелМй берйшфи,,.
жествами М0, М1, М2.
В реализации этого метода большое внимание уделяется эффективности задания множества М1. Нужно, чтобы достаточно быстро реша-
Рисунок 3. Исходный граф.
Начальная вершина А, длины написаны около дуг.
лись задачи и его пополнения, и корректировки при уменьшении расстояния до вершины и поиска в нем вершины с наименьшим расстоянием.
Легко представить себе вариант метода Дейкстры, в котором граф как бы «инвертируется», то есть все дуги направляются в другую сторону. В терминах исходного графа это означает, что строить нужно кратчайшие пути до заданной конечной вершины г'с от всех остальных вершин графа.
4. ОБОБЩЕНИЕ МЕТОДА ДЕЙКСТРЫ
Метод Дейкстры можно применять и в случае, когда часть дерева путей уже вычислена.
Вычислительно такая модификация выглядит очень просто. Для определенности рассмотрим, что нужно сделать в инвер-
Рисунок 5. Дерево кратчайших путей до вершины Р.
Рисунок 4. Дерево кратчайших путей от вершины А до остальных вершин.
тированном методе Дейкстры: при инициализации вычислительного процесса в множество Ых загружаются вершины, расстояния до которых уже вычислены, и если пути, ведущие из таких вершин, изменять нельзя, то запрещается их модификация.
5. РЕШЕНИЕ ЗАДАЧИ ДЛЯ ДВУХ КОНЕЧНЫХ ТОЧЕК
Рассмотрим пример, в котором множество МХ состоит из двух точек, и на нем сначала опробуем эту идею. Пусть граф задан рисунке 3: в нем 25 вершин, 40 дуг, длины которых записаны рядом с дугами, начальной является вершина А, а множество МХ = {Р, X}.
Применяя обычный метод Дейкстры, строим дерево кратчайших расстояний от А до всех вершин графа (рисунок 4).
Рисунок 6. Дерево кратчайших путей до вершины X.
Рисунок 7. Граф избранных вершин с новыми дугами и вершиной О.
Рисунок 8. Дерево кратчайших путей в новом графе.
Но для нас сейчас будет важна возможность строить кратчайшие пути ¿о заданной вершины от всех остальных. На рисунке 5 показано дерево кратчайших путей до вершины Р от всех вершин, где такой путь существует. Естественно, что это условие исключает вершины, расположенные правее или ниже вершины Р. В кружки, обозначающие вершины, вписаны кратчайшие расстояния от них до Р. Аналогично строится и дерево кратчайших путей от всех вершин до вершины X, показанное на рисунке 6. В кружки, обозначающие вершины, вписаны кратчайшие расстояния от них до X.
Соединим теперь данные из рисунков 5 и 6, ограничившись вершинами, от которых есть пути до обеих вершин. Получим рисунок 7, в котором к этим «избранным» вершинам мы добавили еще одну, вершину О. В эту вершину идут дуги из всех имевшихся вершин (правда, на рисунке изображены только пять из них. Кстати, по какому принципу я отобрал эти пять дуг? На самом деле, в графе, который мы строим, должны быть дуги от любой из «избранных» вершин). Смысл новых дуг такой: исходные дуги графа - это дуги обычные, по которым мы добираемся обычным образом, причем, двигаясь по ним, мы движемся к обеим вершинам, в то время как каждая новая дуга символизирует способ передвижения из данной вершины в каждую из требуемых вершин отдельно. Эти отдельные пути сами по себе оптимальны:
Для получившегося графа методом Дейкстры находим дерево кратчайших путей, входящих в вершину О. Оно изображено на рисунке 8. Нам осталось перейти от нового графа к исходному.
В построенном дереве возьмем путь из вершины А. Он кончается «новой» дугой .О. Заменим эту дугу парой путей LP и .X в исходном графе. Мы получим частичное дерево, состоящее из путей А., .Р и .X. Оно изображено на рисунке 9.
6. ПРИНЦИПИАЛЬНОЕ РЕШЕНИЕ ДЛЯ НЕСКОЛЬКИХ КОНЕЧНЫХ ТОЧЕК
А что же делать в случае, когда вершин в множестве Мх больше двух. Случай трех вершин уже выглядит достаточно уг-
Рисунок 9. Оптимальное частичное дерево с путями из А в Р и X.
Таблица 1.
A B C D E F G H I J K L M N P Q R S T U V W X Y Z
P t t t t t t t t t t t t t t t
T t t t t t t t t t t t t t t t t
X t t t t t t t t t t t t t t t
PT t t t t t t t t t t t t
PX t t t t t t t t t
TX t t t t t t t t t t t t
рожающе. Пусть для определенности ={Р, Г, X}.
Порядок вычислений будет такой:
1) Решим задачу для вершин Р, Г и X.
2) Пользуясь полученными данными, решим, как раньше, задачу для пар {Р, Г}, {Р, X} и {Г, X}. Чтобы не запутаться, введем, наконец-то, обозначения. Составим таблицу вычисленных расстояний у], где ^ - подмножество множества Мх, а у - вершина. Можно схематически представить, что уже вычислено к данному моменту (см. таблицу 1).
3) (самое главное) Теперь мы будем вычислять для этой таблицы строку, соответствующую тройке {Р, Г, X}. Для этого впишем в каждую ячейку у[Мх, у] значение шш{аР, аГ, а^}, где
аР = у[Р, у]+ у[7Х у]; аГ = у[Г, у] + у[РХ;]; аX = у[Х у] + v[PX, у] и после этого по методу Дейкстры «досчитаем» получившиеся расстояния.
,,,tc «иуёрлЯЯмм» ¿ермиЛам мм
foßaiu<u еще offtf...
Фактически у нас здесь получается схема динамического программирования, в которой состояниями являются всевозможные пары (s, j), а v - это функция Беллма-на, удовлетворяющая рекуррентному соотношению
v[s, j] = min{aSp//i, aMove), где введенные для удобства aSp//i и aMove обозначают соответственно минимум, который берется по решениям, разбивающим множество s на непустые подмножества, и минимум по переходам в другие вершины с помощью дуги, выходящей из j:
aSp//i = min {v[s', j] + v[s\s', j] I s'с s), aMove = min{cu+ v[s, je(«)] I jb[«] = j).
Если угодно, можно записать это рекуррентное соотношение в более привычном виде
v[s, j] = min{min{ v[s', j] + v[s\s', j] I s с s), {cu+ v[s, je(«)] I jb[«] = j)).
Именно это рекуррентное соотношение лежит в основе нашего расчета. Этот подход вполне приемлем, если память компьютера позволяет хранить таблицу размером m X 2k, где m - число вершин в графе, а k - число вершин в множестве Mx.
7. ПРОГРАММНАЯ РЕАЛИЗАЦИЯ
Программная реализация предлагаемого алгоритма гораздо проще, чем это может показаться с первого взгляда. В ее основе лежит цикл по всем подмножествам множества Mx. Перебор подмножеств конечного множества легко реализуется как перебор характеристических векторов этих подмножеств, то есть всевозможных наборов из нулей и единиц. Если число k не слишком велико, то каждый такой набор
можно считать k-значным двоичным числом, так что перебор осуществляется простым циклом от 1 до kSubset = 2k.
for iSubset := 1 to kSubset do begin
Iteration end;
Оператор Iteration состоит из двух частей: довычисление по методу Дейкстры строки iSubset матрицы v и использование вычисленной строки в подготовке данных для других строк.
Iteration °
Dijkstrification; UseRow;
В методе Дейкстры на самом деле нам не потребуется вводить вершину O с примыкающими к ней новыми дугами: достаточно будет включить все вершины, для которых v уже начало вычисляться, в так называемое множество M1.
Использование вычисленной строки, которая соответствует некоторому подмножеству sCur множества Mx заключается в том, что просматриваются все строки, вычисленные ранее. Каждая такая строка также соответствует некоторому подмножеству sPrev. Если подмножества sCur и sPrev дизъюнктны, то они составляют разбиение подмножества sNew = sCur и sPrev, являющегося их объединением. В таком случае значения v[sNew, j] корректируются по сумме v[sCur, j] + v[sPrev, j]. На самом деле вся работа идет не с подмножествами, а с целыми числами, им соответствующими. Условие дизъюнктности подмножеств прекрасно записывается с помощью операции логического умножения этих чисел. Таким образом, мы имеем
UseRow ° for iPrev := 1 to iSubset-1 do; if (iPrev and iSubset) = 0 then begin
CorrectRow end;
Я не решился здесь же писать и корректировку, но она проста и мы не замедлим ее привести: здесь уже первый индекс массива - не подмножество, а соответствующее ему число.
CorrectRow ° iNew := iPrev + iSubset; for all j do; if (v[iPrev,j]<infinity) and
(v[iSubset,j]<infinity) then begin
v1 := v[iPrev,j] + v[iSubset,j]; if v[iNew,j] > v1 then v[iNew,j] := v1; end;
Заголовок цикла по вершинам зависит от способа задания информации о графе.
Инициализация таблицы v очень проста: сначала вся таблица заполняется «бесконечным» значением, которое мы обозначили через infinity, а затем обнуляются k элементов v[power(i),vert(i)], где power(i) = 2', a vert(i) - истинный номер вершины, стоящей в Mx на месте i.
8. ЧТО ДАЛЬШЕ
Так можно решать задачи с небольшими значениями k; легко получается и можно чуть-чуть зайти за k =20 на больших компьютерах. Между тем, для специальных типов графов можно сильно упростить решение, ограничивая набор рассматриваемых подмножеств.
Если исходный граф планарный, то есть если его можно без пересечений дуг
расположить на плоскости и если все вершины множества находятся на «границе» области, содержащей граф, то можно рассматривать только подмножества, образующие сплошной участок границы.
Например, пусть в графе, показанном на рисунке 3, МХ ={ V, X, X, Р, Е}. Вместо 31 подмножества, необходимого при общем подходе, здесь нам будет достаточно рассмотреть к (к+1)/2 = 15: {V}, {V, X}, {V, X, г}, {V, X, г, Р}, {V, X, г, р, Е}, {X}, {X, г}, {X, г, р}, {X, г, р, Е}, {г}, {г, р}, {г, Р, Е}, {Р}, {Р, Е}, {Е}.
Вопросы: В каком порядке следует рассматривать подмножества в этом частном случае? Какая нумерация подмножеств окажется здесь удобной:
В случае замкнутой границы число подмножеств несколько увеличивается и становится равным к (к - 1) + 1, но практически все возможности эффективного расчета сохраняются.
Было бы интересно предложить другие классы графов, в которых можно ограничить количество перебираемых подмножеств.
Литература
1. Гэри М., Джонсон Д. Вычислительные машины и трудно решаемые задачи. М.: Мир, 1982. (Здесь можно прочесть (совсем немного) про задачу Штейнера - исходную и на графах).
2. Кормен,Т., Лейзерзон Ч., Ривест Р. Алгоритмы: Построение и анализ. М: МЦНМО, 1999. (В этой классической книге есть изложение метода Дейкстры).
3. Наумов Л. А. Метод разбиения задач на подзадачи, рекурсия, «разделяй и властвуй». Динамическое программирование // Компьютерные инструменты в образовании, 2002, № 3-4. С. 94-106.
(Обзорная статья в нашем журнале. В ней подробно рассказано о динамическом программировании).
4. Романовский И. В. Дискретный анализ, 3-е изд., СПб: Невский диалект, 2003. (Учебник, написанный автором данной статьи. В нем затрагиваются темы и динамического
программирования, и метода Дейкстры, и (очень кратко) задачи Штейнера).
(£) Наши авторы, 2004. Our authors, 2004.
Романовский Иосиф Владимирович, доктор физико-математических наук, профессор СПбГУ.