:: алгоритмы  и методы :: :: олимпиадные задачи :: :: связь :: :: о сайте ::
Путь: Математика » Графы и маршруты » Алгоритмы нахождения максимального потока » Алгоритм Голдберга-Тарьяна
  Алгоритм Голдберга-Тарьяна



Алгоритм Голдберга-Таряна (Goldberg-Tarjan)

Алгоритм Голдберга-Таряна (Goldberg-Tarjan).

 

Опубликован в 1986г. Алгоритм основан на идее предпотоков Карзанова, но не использует поиск тупикового потока в фазах. Оценка быстродействия алгоритма: O(n3), но может быть улучшена до O(nmLog(n2/m)), если алгоритм реализован с использованием динамических деревьев Слейтора. Также имеется версия алгоритма для параллельных вычислений с оценкой O(n2Logn) на n процессорах. В основе алгоритма лежат 2 операции: наращивание потока (Push) и изменение метки вершины (Relabel), в связи с этим, метод примененный в алгоритме получил название Push-Relabel. Данный метод внес большой вклад в исследование проблемы максимального потока. Многие современные алгоритмы основаны на Push-Relabel методе.

Рассмотрим неориентированную сеть G. Дадим следующие определения:

Функция избытка: e(v) = - div (v) = ∑ f(v’,v) - ∑ f(v,v’’).

Псевдопотоком назовем функцию f, удовлетворяющую ограничениям пропускных способностей дуг и антисимметричности. Антисимметричность означает что f(v, w) = - f(w, v). Понятие антисимметричности предложено Слейтором и служит двум целям. Во-первых, оно исключает возможность существование положительного потока как в направлении (u, v), так и в направлении (v, u). Во вторых, оно упрощает некоторые определения, например, закон сохранения потока.

Предпотоком является псевдопоток на пути из s в t, для всех вершин которого e(v) ≥ 0 (кроме s).

Под rf(v,w) будем понимать остаточную пропускную способность дуги (v, w). Gf – сеть остаточных пропускных способностей потока f.

Каждой вершине сети сопоставим метку d(v) ≥ 0, обладающую следующими свойствами: d(t) = 0, d(s) = n, для r(v, w)  Gf d(v) ≤ d(w) + 1. Если d(v) < n, то под ней можно понимать дистанцию до стока в Gf, иначе d(v) – n – дистанция до источника Gf.

Назовем вершину активной, если она не является источником или стоком и имеет положительный избыток (e(v) > 0).

 

Рассмотрим базовые операции:

Push(v, w). Увеличивает поток из v в w на q, где q = min{e(v), r(v, w)}. Очевидно, что вершина v должна быть активной, дуга (v, w) не насыщенной, а d(v) = d(w) + 1.

Relabel(v). Обновление метки вершины v. Вершина должна быть активной, и для w с r(v, w) > 0 должно выполнятся d(v) ≤ d(w). Relabel устанавливает d(v) = min(d(w)+1| (v,w)  Ef), где Ef – множество дуг сети остаточных пропускных способностей относительно потока f.

Push_Relabel(v).

Пока e(v) не станет нулевой или не изменится d(v) выполняем внутренний цикл:

Пусть (v, w) – текущая дуга в списке a(v). Рассмотрим 2 случая.

a). Операция Push применима к (v, w). В этом случае производим увеличение потока и, если вершина w стала активной, помещаем ее в конец очереди X.

б). Push не применима к (v, w). Тогда, если (v, w) не последняя дуга в a(v), то делаем текущей следующую дугу списка. Если же (v, w) последняя дуга, то выполняем Relabel(v) и выбираем текущей первую дугу в a(v). В [10] доказана применимость Relabel к v после обработки всех дуг a(v).

 

Общая логика работы алгоритма такова:

Инициализация.

В смежные источнику вершины v посылается поток величины c(s, v). Поток на остальных дугах нулевой. Смежные с источником вершины становятся избыточными, избыток в остальных вершинах отсутствует. Расставим метки: d(s) = n, для всех остальных вершин d(v) = 0.[1] Поместим все активные вершины в очередь X.[2] Также нам потребуются списки всех исходящих дуг a(v) для каждой вершины. В этих списках будет выбрана “текущая дуга” (при инициализации текущей дугой выбирается первая дуга в списке).

 

Главный цикл.

До тех пор, пока очередь X не пуста, извлекаем вершину v из очереди и выполняем Push_Relabel. Проверим, не перестала ли вершина v быть активной. Если нет – добавляем ее в конец X. Переходим к следующей вершине очереди X.

 

Применение динамических деревьев в алгоритме Голдберга-Таряна.

 

Без использования деревьев Слейтора-Таряна алгоритм имеет оценку быстродействия O(n3), что повторяет достижение Карзанова. Рассмотрим набор корневых, не имеющих общих вершин деревьев. Каждой вершине сопоставим число g(v), которое может дополнительно принимать значение + ∞ и - ∞. g(v) – это пропускная способность исходящей из v дуги. Пусть p(v) – множество предков вершины v, P(v) – смежный с v предок. Примем условие, что любая вершина v  для самой себя одновременно и предок, и потомок. Введем следующие операции:

Root(v) – аналогична операции в деревьях Слейтора-Таряна.

Size(v) – возвращает число вершин в дереве, которому принадлежит v.

Value(v) – аналог процедуры Cost для дуг. Возвращает g(v).

Find_min(v) – возвращает ближайшего к корню предка w  p(v), с минимальным g(w).

Update(v, x) – добавляет x к g(w), для каждого w  p(v). (- ∞ + ∞ = 0).

Link(v, w) – соединяет деревья, которым принадлежат вершины v и w, делая v потомком w. Если v и w принадлежат одному дереву или v не является корнем, то процедура завершается, не изменяя деревьев.

Cut(v) – Делит дерево на два, удаляя дугу из вершины v к ее предку. Если v – корень, то процедура не применяется.

 

Динамические деревья используются для хранения текущих дуг вершин. Во время инициализации все вершины представляют собой набор корней деревьев без дуг. g(v) всех вершин = ∞. Дуги дерева являются подмножеством текущих дуг вершин сети. В дерево могут быть добавлены только допустимые текущие дуги. Назовем текущую дугу (v, w) вершины v  V \ {s, t} допустимой, если d(v) = d(w) + 1 и rf(v, w) > 0. Дуга (v, w) добавляется в дерево так, что w = P(v). Ограничим размер дерева числом k, где k = n2/m (объяснение подобного решения см. в [10]).

Опишем следующие процедуры:

1. Clear(v) – удаление всех дуг с нулевой пропускной способностью.

 Пока Value(Find_min(v)) = 0 выполнять: u := Find_min(v); Cut(u); Update (u, + ∞).

2. Send(v) - “проталкивает” избыток вершины v в корень дерева:

До тех пор, пока вершина v не стала корнем дерева, или избыток вершины не стал равен 0, выполняем Update(v, -q),где q = min{e(v), Value( Find_min(v) )} и Clear(v). Процедура Send применима только к активным вершинам.

3. TreePush_Relabel(v) - применима только если v активна и является корнем дерева. Рассмотрим текущую дугу (v, w) вершины v. Имеются 4 случая:

а.) Дуга допустима и Size(v) + Size(w) ≤ k. Тогда делаем v потомком w, выполняя Update(v, - ∞) (чтобы получить g(v) = 0), Update(v, rf(v, w)) и Link(v, w). И “проталкиваем” поток: send(v)

б). Дуга допустима и Size(v) + Size(w) > k. Выполняем Send(w).

в). Дуга не допустима и не является последней в списке a(v). Выбираем следующую текущую дугу из списка.

г). Дуга не допустима и последняя в a(v). Выбираем текущей дугой первую дугу списка. Выполняем Cut(v, w). Для всех потомков u вершины v выполняем Update(u, ∞). В завершение, вызываем Relabel(v).

 

Заменив в главном цикле процедуру Push_Relabel на TreePush_Relabel, мы получим алгоритм поиска максимального потока для динамического дерева.

 

 

В своей статье Голдберг и Тарян предложили также ряд эвристик, которые не влияют на асимптотическую оценку алгоритма, но могут увеличить его быстродействие на практике. Например периодическое обновление меток вершин в сети остаточных пропускных способностей, с помощью поиска в ширину из источника в сток и из стока в источник. Такой поиск позволит получить для вершины v расстояние до стока d(v, t) и до источника d(v, s). В качестве d(v) следует выбрать min{d(v, s) + n, d(v, t)}. Есть несколько вариантов выбора момента проведения этого уточнения. Например, через каждые k операций Relabel, или каждый раз, когда насыщается дуга, ведущая в сток, либо поток по исходящей из источника дуге окажется нулевым.

Благодаря “гибкости” своей реализации, возможности распараллеливания вычислений и высокому быстродействию на большинстве типов сетей, Push-Relabel методы получили широкое распространение на практике.

 



[1] Можно инициализировать сеть и более точными оценками дистанции. Если d(v) < n, то d(v) = расстоянию до t, иначе d(v) = расстоянию до s + n. Такая модификация дает большее быстродействие, но требует более точно выбирать текущие дуги. Текущая дуга должна быть не насыщенной и вести к вершине с меньшей меткой d(v). При выполнении Relabel(v), следует для всех вершин u с текущими дугами (u, v) выбрать новые текущие дуги.

[2] Кроме очереди, Голдберг и Тарян рассматривали также другие стратегии выбора обрабатываемой вершины. Лучшие результаты были получены при выборе вершин с максимальной d(v) и “методе волны”.