10
правок
Изменения
Нет описания правки
'''Алгоритм Каргера''' (англ. ''Karger's algorithm'') {{---}} рандомизированный алгоритм для нахождения минимального разреза в связном графе. Он был разработан Дэвидом Каргером (англ. ''David Karger'') в 1993 году.
== Основные определения ==
Пусть <tex>G</tex> {{---}} неориентированный [[Основные_определения_теории_графов#def1 | мультиграф ]] с множеством вершин <tex>V</tex> и множеством ребер <tex>E</tex>, и <tex>|V| = n</tex>, <tex>|E| = m</tex>.
{{Определение
|definition=
== Алгоритм ==
Пусть нам дан граф <tex>G = \{V, E\}</tex>. <code> '''int''' minCut(G): answer = INF;<tex>\infty</tex> '''for ''' i = 1..'''to''' count curCut = getCut(G); '''if ''' curCut < answer answer = curCut; '''return ''' answer;</code>Так как алгоритм вероятностный и функция <tex>\mathrm{getCut}</tex> возвращает вес случайного потока, а не минимального, то для того, чтобы наверняка найти вес минимального, необходим вызвать эту функцию <tex>count</tex> раз. Какое конкретное значение <tex>count</tex> выбрать будет описано ниже.Реализация функции <tex>\mathrm{getCut}</tex> {{---}} это и есть основа алгоритма. Будем действовать следующим образом: пока вершин больше двух, будем выбирать случайное ребро и стягивать его. Когда в графе останется две вершины, то количество ребер между этими вершинами будет равно весу некоторого разреза исходного графа. <code> '''int''' getCut(graph): n G = graph vertexCount = количество вершин в G; '''while n ''' vertexCount > 2 e edge = случайное ребро из G; contract(eedge);<font color="darkgreen">//стягиваем ребро edge</font> nvertexCount--; m edgeCount = количество ребер в G; '''return m;''' edgeCount</code>
== Корректность алгоритма ==
Докажем корректность алгоритма. Для удобства доказательства и оценок вероятностей, будем считать, что мы ищем не просто вес минимального разреза, а величину конкретного минимального разреза. То есть зафиксируем какой-то один из минимальных разрезов, и если функция <tex>\mathrm{getCut}</tex> вернет правильный вес, но посчитает его на другом минимальном разрезе, то будем считать, что ответ получен неверный. Это условие не ухудшает оценки вероятностей и время работы алгоритма.
{{Лемма
|about = 1
|about = 4
|id = Лемма4
|statement = Функция <tex>\mathrm{getCut}</tex> после своего выполнения получит стянутый граф <tex>G'</tex> соответствующий конкретному разрезу <tex>\langle A, B \rangle</tex> исходного графа <tex>G</tex> тогда и только тогда, когда ни одно ребро, принадлежащее разрезу <tex>\langle A, B \rangle</tex>, не будет стянуто.
|proof =
{{Теорема
|statement =
Для вероятности <tex>p</tex> того, что функция <tex>\mathrm{getCut}</tex> после своего выполнения получит стянутый граф <tex>G'</tex> соответствующий конкретному разрезу <tex>\langle A, B \rangle</tex> исходного графа <tex>G</tex> верна следующая оценка снизу {{---}} <tex>p(\langle A, B \rangle) \geq</tex> <tex dpi="150">\frac{2}{n^2}</tex>.
|proof =
По [[#Лемма4|лемме 4]] искомый стянутый граф будет получен тогда и только тогда, когда ни одно из ребер разреза не будет стянуто. Так как на каждой итерации алгоритма мы случайно выбираем ребро для стягивания, то можно оценить вероятность того, что ни одно из ребер разреза не будет стянуто. Каждое стягивание уменьшает количество ребер на <tex>1</tex>. Таким образом после <tex>i</tex>-ой итерации алгоритма количество вершин в текущем графе будет равно <tex>n - i + 1</tex>. Рассчитаем вероятность того, что на <tex>i</tex>-ой итерации будет стянуто ребро из разреза <tex>\langle A, B \rangle</tex>. Пусть <tex>w = w(A, B)</tex>. По [[#Лемма2|лемме 2]] величина минимального потока в текущем графе будет на меньше, чем <tex>w</tex>, и в нем будет не менее <tex dpi="150">\frac{w(n-i+1)}{2}</tex> вершин по [[#Лемма3|лемме 3]]. Тогда вероятность того, что будет стянуто ребро из разреза <tex>\langle A, B \rangle</tex>, при условии, что до этого не было стянуто ни одно из ребер разреза, будет не более
}}
Таким образом, мы видим, что вероятность получить правильный ответ после единичного запуска функции <tex>\mathrm{getCut}</tex> очень мала. Однако, вызвав эту функцию <tex>count</tex> раз мы увеличим эту вероятность. Рассчитаем значение <tex>count</tex> такое, чтобы вероятность успеха была близка к <tex>1</tex>.
Для этого воспользуемся следующим неравенством:
Пусть <tex>count = n^2\ln(n)</tex>. Тогда в силу указанного выше неравенства:
<tex dpi="140">(1-\frac{2}{n^2})^{count } \leq e^{count \cdot (-\frac{2}{n^2})} = e^{-2 \ln(n)} = \frac{1}{n^2}</tex>.
То есть, если вызвать функцию <tex>\mathrm{getCut}</tex> <tex>n^2\ln(n)</tex> раз, и после каждой итерации запоминать минимум, то вероятность того, что найденный ответ будет неверен <tex> \leq \frac{1}{n^2}</tex>. Даже при небольших значениях <tex>n</tex> алгоритм практически гарантированно выдает правильный ответ.
== Оценка времени работы алгоритма ==
Время работы функции <tex>\mathrm{getCut}</tex> {{---}} <tex>O(n^2)</tex>. Функция работает <tex>O(n^2\log(n))</tex> раз. Итоговое время работы {{---}} <tex>O(n^4 \log(n))</tex>.
== Оптимизация алгоритма ==
Каргер совместно со Штейном (англ. ''Stein'') придумали оптимизацию алгоритма Каргера, значительно ускоряющую время его работы. Новый алгоритм называется в честь обоих создателей {{---}} алгоритм Каргера-Штейна (англ. ''Karger-Stein algorithm''). Его суть заключается в следуюшем. Заметим, что вероятность стягивания вершины, принадлежащей минимальному разрезу, в начале выполнения функции <tex>\mathrm{getCut}</tex> довольна мала, в то время, как вероятность стянуть ребро, которое не следует стягивать, ближе к концу работы функции существенно возрастает. Тогда будем использовать следующую рекурсивную версию алгоритма:# Запускаем функцию <tex>\mathrm{getCut}</tex> и стягиваем ребра до тех пор, пока не останется <tex>\frac{n}{\sqrt{2}}</tex> вершин.
# Запускаем независимо эту же функцию для получившегося графа дважды и возвращаем минимальный из ответов.
Такая модификация алгоритма выдает правильный ответ с точностью, не менее <tex>\frac{1}{\log(n)}</tex>. Время работы функции <tex>\mathrm{getCut}</tex> вычисляется рекурсивной функцией:
<tex>T(n) = O(n^2) + T(n/\sqrt{2}) = O(n^2\log(n))</tex>.