Венгерский алгоритм решения задачи о назначениях — различия между версиями
Murtaught (обсуждение | вклад) м (→Некоторые полезные утверждения) |
Da1s111 (обсуждение | вклад) (→Алгоритм за O(n^3)) |
||
| Строка 72: | Строка 72: | ||
== Алгоритм за <tex>O(n^3)</tex> == | == Алгоритм за <tex>O(n^3)</tex> == | ||
| − | + | <tex>a[1 \dots n][1 \dots m]</tex> {{---}} прямоугольная входная матрица, где <tex>n \leq m</tex>. Матрица хранится в 1-индексации. | |
| − | + | <tex>u[0 \dots n], v[0 \dots n]</tex> {{---}} массивы потенциалов. Изначально нулевые, что верно для матрицы, состоящей из 0 строк. | |
| − | + | <tex>p[0 \dots m]</tex> {{---}} массив паросочетания. Для каждого стобца <tex>i = 0 \dots m</tex> он хранит номер соответствующей выбранной строки <tex>p[i]</tex> (или 0, если ничего не выбрано). Полагаем, что <tex>p[0]</tex> равно номеру рассматриваемой строки. | |
| − | + | <tex>minv[1 \dots m]</tex> {{---}} массив, хранящий для каждого столбца j вспомогательные минимумы, необходимые для быстрого пересчета потенциала. | |
| − | + | <tex>minv[j] = \min_{i \in Z_1}\{a[i][j] - u[i] - v[j]\}</tex> | |
| − | + | <tex>way[1 \dots m]</tex> {{---}} массив, содержащий информацию о том, где эти минимумы достигаются, чтобы мы могли впоследствии восстановить увеличивающую цепочку. | |
| − | + | '''hungarianAlgorithm'''(a): | |
| − | ''' | + | '''for''' i = 1 '''to''' n: |
| − | + | p[0] = i | |
| − | + | j0 = 0 | |
| − | + | '''for''' i = 0 '''to''' m + 1: | |
| − | + | minv[i] = <tex>\infty</tex> | |
| − | + | used[i] = false | |
| − | + | '''while''' true: | |
| − | + | used[j0] = true | |
| − | + | i0 = p[j0] | |
| + | <tex>\delta</tex> = <tex>\infty</tex> | ||
| + | '''for''' j = 1 '''to''' m: | ||
| + | '''if''' used[j] == 0: | ||
| + | cur = a[i0][j] - u[i0] - v[j] | ||
| + | '''if''' cur < minv[j]: | ||
| + | minv[j] = cur | ||
| + | way[j] = j0 | ||
| + | '''if''' minv[j] < <tex>\delta</tex>: | ||
| + | <tex>\delta</tex> = minv[j] | ||
| + | j1 = j | ||
| + | '''for''' j = 0 '''to''' m: | ||
| + | '''if''' used[j]: | ||
| + | u[p[j]] += <tex>\delta</tex> | ||
| + | v[j] -= <tex>\delta</tex>; | ||
| + | '''else''': | ||
| + | minv[j] -= <tex>\delta</tex> | ||
| + | j0 = j1; | ||
| + | '''if''' p[j0] != 0: | ||
| + | '''break''' | ||
| + | '''while''' true: | ||
| + | j1 = way[j0] | ||
| + | p[j0] = p[j1] | ||
| + | j0 = j1 | ||
| + | '''if''' j0 <tex>\neq</tex> 0: | ||
| + | '''break''' | ||
== Ссылки == | == Ссылки == | ||
Версия 05:32, 14 января 2016
Венгерский алгоритм — алгоритм, решающий задачу о назначениях за полиномиальное время. Оригинальная версия была придумана и разработана Х. Куном в 1955 году и имела асимптотику , но позже Эдмонс и Карп (а также, независимо от них, Томидзава) показали, что можно улучшить ее до .
Содержание
Постановка задачи
Пусть дан взвешенный полный двудольный граф c целыми весами ребер , нужно найти в нем полное паросочетание минимального веса. Вес паросочетания определяется как сумма весов его ребер. Далее будем обозначать левую и правую доли графа за и соответственно, вес ребра — как .
Некоторые полезные утверждения
| Лемма: |
Если веса всех ребер графа, инцидентных какой-либо вершине, изменить (увеличить или уменьшить) на одно и то же число, то в новом графе оптимальное паросочетание будет состоять из тех же ребер, что и в старом. |
| Доказательство: |
| Полное паросочетание для каждой вершины содержит ровно одно ребро, инцидентное этой вершине. Указанная операция изменит на одно и то же число вес любого паросочетания. Значит, ребро, которое принадлежало оптимальному паросочетанию в старом графе, в новом графе тоже будет ему принадлежать. |
Далее будем рассматривать только графы с неотрицательной весовой функцией, так как, согласно этой лемме, задачу о назначениях на остальных графах можно свести к задаче о назначениях на них.
| Лемма: | |||||||||
Выделим в множествах и подмножества . Пусть . Прибавим ко всем весам ребер, инцидентных вершинам из . Затем отнимем от всех весов ребер, инцидентных вершинам из (далее для краткости эта операция обозначается как ). Тогда:
| |||||||||
| Доказательство: | |||||||||
|
Рассмотрим матрицу весов графа. Не умаляя общности, можно сказать, что множества и состоят из первых элементов множеств и соответственно (мы упорядочиваем множества по номерам вершин). Тогда вся матрица делится на 4 блока: | |||||||||
| Лемма: |
Если веса всех ребер графа неотрицательны и некоторое полное паросочетание состоит из ребер нулевого веса, то оно является оптимальным. |
| Доказательство: |
| Действительно, паросочетание с какими-то другими весами ребер имеет больший вес и оптимальным не является. |
Общий метод
Доказанные ранее утверждения позволяют придумать схему алгоритма, решающего задачу о назначениях: нужно найти полное паросочетание из ребер нулевого веса в графе, полученном из исходного преобразованиями, описанными в первых двух леммах.
Алгоритм, решающий задачу, работает с графом, как с матрицей весов.
- Вычитаем из каждой строки значение ее минимального элемента. Теперь в каждой строке есть хотя бы один нулевой элемент.
- Вычитаем из каждого столбца значение его минимального элемента. Теперь в каждом столбце есть хотя бы один нулевой элемент.
- Ищем в текущем графе полное паросочетание из ребер нулевого веса:
-
- Если оно найдено, то желаемый результат достигнут, алгоритм закончен.
- В противном случае, покроем нули матрицы весов минимальным количеством строк и столбцов (это не что иное, как нахождение минимального вершинного покрытия в двудольном графе). Пусть и — множества вершин минимального вершинного покрытия из левой и правой долей (то есть, строк и столбцов) соответственно, тогда применим преобразование . Для этого преобразования будет минимумом по всем ребрам между и , то есть, ребер нулевого веса здесь нет, поэтому, после его выполнения в матрице весов появится новый нуль. После этого перейдем к шагу 1.
Анализ времени работы
Поиск максимального паросочетания или минимального вершинного покрытия в двудольном графе совершается за операций. При каждом повторении шагов 1-4 в матрице весов появляется новый нуль. Этот нуль соответствует некоторому новому ребру между вершинами из множеств и . Всего в графе n^2 ребер, значит, всего будет совершено не более итераций внешнего цикла. Поэтому, верхняя оценка времени работы данного метода — . Более точная оценка довольно сложна и зависит от порядка чисел в матрице весов графа.
Алгоритм за
— прямоугольная входная матрица, где . Матрица хранится в 1-индексации.
— массивы потенциалов. Изначально нулевые, что верно для матрицы, состоящей из 0 строк.
— массив паросочетания. Для каждого стобца он хранит номер соответствующей выбранной строки (или 0, если ничего не выбрано). Полагаем, что равно номеру рассматриваемой строки.
— массив, хранящий для каждого столбца j вспомогательные минимумы, необходимые для быстрого пересчета потенциала.
— массив, содержащий информацию о том, где эти минимумы достигаются, чтобы мы могли впоследствии восстановить увеличивающую цепочку.
hungarianAlgorithm(a):
for i = 1 to n:
p[0] = i
j0 = 0
for i = 0 to m + 1:
minv[i] =
used[i] = false
while true:
used[j0] = true
i0 = p[j0]
=
for j = 1 to m:
if used[j] == 0:
cur = a[i0][j] - u[i0] - v[j]
if cur < minv[j]:
minv[j] = cur
way[j] = j0
if minv[j] < :
= minv[j]
j1 = j
for j = 0 to m:
if used[j]:
u[p[j]] +=
v[j] -= ;
else:
minv[j] -=
j0 = j1;
if p[j0] != 0:
break
while true:
j1 = way[j0]
p[j0] = p[j1]
j0 = j1
if j0 0:
break
Ссылки
Литература
- Асанов М., Баранский В., Расин В. — Дискретная математика: Графы, матроиды, алгоритмы — 2010, 368 стр.