В теории графов алгоритм цветения — это алгоритм построения максимальных паросочетаний на графах . Алгоритм был разработан Джеком Эдмондсом в 1961 году [1] и опубликован в 1965 году. [2] Для заданного общего графа G = ( V , E ) алгоритм находит паросочетание M такое, что каждая вершина в V инцидентна не более чем одному ребру в M и | M | максимизируется. Паросочетание строится путем итеративного улучшения исходного пустого паросочетания вдоль увеличивающихся путей в графе. В отличие от двудольного паросочетания, ключевая новая идея заключается в том, что цикл нечетной длины в графе (цветение) сжимается до одной вершины, при этом поиск итеративно продолжается в сжатом графе.
Алгоритм выполняется за время O (| E || V | 2 ) , где | E | — число ребер графа, а | V | — число его вершин . Лучшее время выполнения для той же задачи может быть достигнуто с помощью гораздо более сложного алгоритма Микали и Вазирани. [3]
Основная причина, по которой алгоритм bloom важен, заключается в том, что он дал первое доказательство того, что сопоставление максимального размера может быть найдено с использованием полиномиального количества времени вычислений. Другая причина заключается в том, что он привел к линейному программированию полиэдрального описания сопоставления политопа , что дало алгоритм для сопоставления минимального веса . [4] Как пояснил Александр Шрайвер , дополнительная значимость результата исходит из того факта, что это был первый политоп, доказательство целостности которого «не просто следует из полной унимодулярности , а его описание стало прорывом в полиэдральной комбинаторике ». [5]
При условии G = ( V , E ) и соответствия M из G вершина v открыта , если ни одно ребро M не инцидентно v . Путь в G является чередующимся путем , если его ребра попеременно не находятся в M и в M (или в M и не в M ). Увеличивающий путь P является чередующимся путем, который начинается и заканчивается в двух различных открытых вершинах. Обратите внимание, что количество несоответствующих ребер в увеличивающем пути на единицу больше количества сопоставленных ребер, и, следовательно, общее количество ребер в увеличивающем пути нечетно. Соответствующее увеличение вдоль увеличивающего пути P является операцией замены M новым сопоставлением
По лемме Бержа , паросочетание M является максимальным тогда и только тогда, когда в G нет M -увеличивающего пути . [6] [7] Следовательно, либо паросочетание является максимальным, либо его можно увеличить. Таким образом, начиная с начального паросочетания, мы можем вычислить максимальное паросочетание, увеличивая текущее паросочетание увеличивающими путями, пока мы можем их найти, и возвращаться всякий раз, когда не остается увеличивающих путей. Мы можем формализовать алгоритм следующим образом:
ВХОД: Граф G , начальное соответствие M на G ВЫХОД: максимальное соответствие M* на G Функция A1 find_maximum_matching ( G , M ) : M* A2 P ← find_augmenting_path ( G , M )A3 если P непустое, то A4 возвращает find_maximum_matching ( G , дополняет M по P )A5 иначе A6 вернуть MA7 конец, если A8 конец функция
Нам еще предстоит описать, как эффективно находить увеличивающиеся пути. Подпрограмма для их поиска использует цветки и сокращения.
При наличии G = ( V , E ) и соответствия M из G цветком B называется цикл в G, состоящий из 2 k + 1 ребер, из которых ровно k принадлежат M , и где одна из вершин v цикла ( основание ) такова, что существует чередующийся путь четной длины ( стебель ) от v до открытой вершины w .
В поисках цветов:
Определим сжатый граф G' как граф, полученный из G путем сжатия каждого ребра B , и определим сжатое паросочетание M' как паросочетание G', соответствующее M.
G' имеет M' -увеличивающий путь тогда и только тогда, когда G имеет M ' -увеличивающий путь, и что любой M' -увеличивающий путь P' в G' может быть поднят до M -увеличивающего пути в G путем отмены сокращения B так, чтобы сегмент P' (если таковой имеется), проходящий через v B, был заменен соответствующим сегментом, проходящим через B . [8] Более подробно:
Таким образом, цветки могут быть сокращены и поиск может быть выполнен в сокращенных графах. Это сокращение является ядром алгоритма Эдмондса.
Поиск увеличивающегося пути использует вспомогательную структуру данных, состоящую из леса F , отдельные деревья которого соответствуют определенным частям графа G. Фактически, лес F тот же самый, который использовался бы для поиска максимальных паросочетаний в двудольных графах (без необходимости сокращающихся цветков). В каждой итерации алгоритм либо (1) находит увеличивающийся путь, (2) находит цветок и рекурсирует на соответствующий сокращенный граф, либо (3) приходит к выводу, что увеличивающихся путей нет. Вспомогательная структура строится с помощью инкрементной процедуры, обсуждаемой далее. [8]
Процедура построения рассматривает вершины v и ребра e в G и постепенно обновляет F по мере необходимости. Если v находится в дереве T леса, мы root(v)
обозначим корень T. Если и u, и v находятся в одном и том же дереве T в F , мы distance(u,v)
обозначим длину уникального пути от u до v в T.
ВХОД: Граф G , соответствующий M в G ВЫХОД: Дополняющий путь P в G или пустой путь, если ничего не найденоB01 функция find_augmenting_path ( G , M ) : P
B02 F ← пустой лесB03 снять отметку со всех вершин и ребер в G , отметить все ребра M
B05 для каждой открытой вершины v выполнить
B06 создать одноэлементное дерево { v } и добавить дерево к F
B07 завершить для
B08 пока есть неотмеченная вершина v в F с расстоянием (v, root(v)) четным выполнить
B09 пока существует неотмеченное ребро e = { v , w } выполнить
B10 если w не находится в F , то // w сопоставлено, поэтому добавить сопоставленное ребро e и w к F
B11 x ← вершина, сопоставленная с w в M
B12 добавить ребра { v , w } и { w , x } к дереву v
B13 иначе
B14 если расстояние (w, root(w)) нечетное , то // Ничего не делать.B15 else
B16 if root(v) ≠ root(w) then // Сообщить о увеличивающемся пути в F { e }.B17 P ← путь ( корень ( v ) → ... → v ) → ( w → ... → корень ( w ))B18 return P
B19 else // Сжимаем цветок в G и ищем путь в сжатом графе.B20 B ← цветение, образованное e и ребрами на пути v → w в T
B21 G', M' ← сжать G и M с помощью B
B22 P' ← найти_увеличивающий_путь ( G' , M' )B23 P ← поднять P' до G
B24 вернуть P
B25 конец, если
B26 конец, если
B27 конец, если
B28 отметить ребро e
B29 конец, пока
B30 отметить вершину v
B31 конец, пока
B32 вернуть пустой путьB33 конечная функция
Следующие четыре рисунка иллюстрируют выполнение алгоритма. Пунктирные линии обозначают ребра, которые в данный момент отсутствуют в лесу. Сначала алгоритм обрабатывает ребро вне леса, которое вызывает расширение текущего леса (линии B10 – B12).
Далее он обнаруживает цветение и сжимает график (линии B20 – B21).
Наконец, он находит увеличивающийся путь P′ в сжатом графе (строка B22) и поднимает его до исходного графа (строка B23). Обратите внимание, что способность алгоритма сжимать цветки здесь имеет решающее значение; алгоритм не может найти P в исходном графе напрямую, потому что в строке B17 алгоритма рассматриваются только ребра вне леса между вершинами на четных расстояниях от корней.
Лес F , построенный функцией find_augmenting_path()
, является чередующимся лесом. [9]
Каждая итерация цикла, начинающегося со строки B09, либо добавляет к дереву T в F (строка B10), либо находит увеличивающий путь (строка B17), либо находит цветок (строка B20). Легко видеть, что время выполнения равно .
Когда G двудольный , в G нет нечетных циклов . В этом случае цветки никогда не будут найдены, и можно просто удалить строки B20 – B24 алгоритма. Таким образом, алгоритм сводится к стандартному алгоритму построения паросочетаний максимальной мощности в двудольных графах [7] , где мы многократно ищем увеличивающий путь простым обходом графа: это, например, случай алгоритма Форда–Фалкерсона .
Проблему сопоставления можно обобщить, назначив веса ребрам в G и запросив множество M , которое производит сопоставление максимального (минимального) общего веса: это проблема сопоставления максимального веса . Эту задачу можно решить с помощью комбинаторного алгоритма, который использует невзвешенный алгоритм Эдмондса в качестве подпрограммы. [6] Колмогоров предоставляет эффективную реализацию этого на C++. [10]