Венгерский алгоритм решения задачи о назначениях — различия между версиями
Da1s111 (обсуждение | вклад) м (→Алгоритм за O(n^3)) |
Da1s111 (обсуждение | вклад) |
||
Строка 1: | Строка 1: | ||
− | Венгерский алгоритм — алгоритм, решающий задачу о назначениях за полиномиальное время. Оригинальная версия была придумана и разработана Х. Куном в 1955 году и имела асимптотику <tex> O(n^4) </tex>, но позже Эдмонс и Карп (а также, независимо от них, Томидзава) показали, что можно улучшить ее до <tex> O(n^3) </tex>. | + | '''Венгерский алгоритм''' (англ. '''Hungarian algorithm''') — алгоритм, решающий задачу о назначениях за полиномиальное время. Оригинальная версия была придумана и разработана Х. Куном в 1955 году и имела асимптотику <tex> O(n^4) </tex>, но позже Эдмонс и Карп (а также, независимо от них, Томидзава) показали, что можно улучшить ее до <tex> O(n^3) </tex>. |
− | + | {{Задача | |
− | = | + | |definition = Пусть дан [[Основные определения теории графов|взвешенный полный двудольный граф]] c целыми весами ребер <tex> K_{n, n} </tex>, нужно найти в нем [[Паросочетания: основные определения, теорема о максимальном паросочетании и дополняющих цепях|полное паросочетание минимального веса]]. Вес паросочетания определяется как сумма весов его ребер. Далее будем обозначать левую и правую доли графа за <tex> X </tex> и <tex> Y </tex> соответственно, вес ребра <tex> xy </tex> — как <tex> c(xy) </tex>. |
− | + | }} | |
− | Пусть дан [[Основные определения теории графов|взвешенный полный двудольный граф]] c целыми весами ребер <tex> K_{n, n} </tex>, нужно найти в нем [[Паросочетания: основные определения, теорема о максимальном паросочетании и дополняющих цепях|полное паросочетание минимального веса]]. Вес | ||
− | == | + | == Вспомогательные леммы == |
{{Лемма | {{Лемма | ||
Строка 18: | Строка 17: | ||
{{Лемма | {{Лемма | ||
|statement= | |statement= | ||
− | Выделим в множествах <tex>X</tex> и <tex>Y</tex> подмножества <tex>X', Y'</tex>. Пусть <tex>d = \min \{c(xy) | + | Выделим в множествах <tex>X</tex> и <tex>Y</tex> подмножества <tex>X', Y'</tex>. Пусть <tex>d = \min \{c(xy) \mid x \in X \setminus X', y \in Y'\}</tex>. Прибавим <tex> d </tex> ко всем весам ребер, инцидентных вершинам из <tex>X'</tex>. Затем отнимем <tex> d </tex> от всех весов ребер, инцидентных вершинам из <tex>Y'</tex> (далее для краткости эта операция обозначается как <tex> X' \uparrow\downarrow Y' </tex>). Тогда: |
# Веса всех ребер графа останутся неотрицательными. | # Веса всех ребер графа останутся неотрицательными. | ||
# Веса ребер вида <tex>xy</tex>, где <tex>x \in X', y \in Y'</tex> или <tex>x \in X \backslash X', y \in Y \backslash Y'</tex>, не изменятся. | # Веса ребер вида <tex>xy</tex>, где <tex>x \in X', y \in Y'</tex> или <tex>x \in X \backslash X', y \in Y \backslash Y'</tex>, не изменятся. | ||
Строка 72: | Строка 71: | ||
== Алгоритм за <tex>O(n^3)</tex> == | == Алгоритм за <tex>O(n^3)</tex> == | ||
− | <tex> a[1 \dots n][1 \dots m] </tex> {{---}} прямоугольная входная матрица, где <tex> n \ | + | <tex> \mathtt{a[1 \dots n][1 \dots m]} </tex> {{---}} прямоугольная входная матрица, где <tex> \mathtt{n \leqslant m} </tex>. Матрица хранится в 1-индексации. |
− | <tex> u[0 \dots n], v[0 \dots n] </tex> {{---}} массивы потенциалов | + | <tex> \mathtt{u[0 \dots n], v[0 \dots n]} </tex> {{---}} массивы потенциалов. |
− | <tex> p[0 \dots m] </tex> {{---}} массив паросочетания. Для каждого стобца <tex> i = 0 \dots m </tex> он хранит номер соответствующей выбранной строки <tex> p[i] </tex> (или 0, если ничего не выбрано). Полагаем, что <tex> p[0] </tex> равно номеру рассматриваемой строки. | + | <tex> \mathtt{p[0 \dots m]} </tex> {{---}} массив паросочетания. Для каждого стобца <tex> \mathtt{i = 0 \dots m} </tex> он хранит номер соответствующей выбранной строки <tex> \mathtt{p[i]} </tex> (или 0, если ничего не выбрано). Полагаем, что <tex> \mathtt{p[0]} </tex> равно номеру рассматриваемой строки. |
− | <tex> minv[1 \dots m] </tex> {{---}} массив, хранящий для каждого столбца j вспомогательные минимумы, необходимые для быстрого пересчета потенциала. | + | <tex> \mathtt{minv[1 \dots m]} </tex> {{---}} массив, хранящий для каждого столбца j вспомогательные минимумы, необходимые для быстрого пересчета потенциала. |
− | <tex> minv[j] = \min_{i \in Z_1}\{a[i][j] - u[i] - v[j]\} </tex> | + | <tex> \mathtt{minv[j] = \min_{i \in Z_1}\{a[i][j] - u[i] - v[j]\}} </tex> |
− | <tex> way[1 \dots m] </tex> {{---}} массив, содержащий информацию о том, где эти минимумы достигаются, чтобы мы могли впоследствии восстановить [[Паросочетания: основные определения, теорема о максимальном паросочетании и дополняющих цепях|увеличивающую цепочку]]. | + | <tex> \mathtt{way[1 \dots m]} </tex> {{---}} массив, содержащий информацию о том, где эти минимумы достигаются, чтобы мы могли впоследствии восстановить [[Паросочетания: основные определения, теорема о максимальном паросочетании и дополняющих цепях|увеличивающую цепочку]]. |
'''function''' hungarianAlgorithm(a): | '''function''' hungarianAlgorithm(a): | ||
Строка 89: | Строка 88: | ||
p[0] = i | p[0] = i | ||
j0 = 0 | j0 = 0 | ||
− | + | заполняем массивы minv {{---}} <tex> \infty </tex>, used {{---}} false | |
− | |||
− | |||
<font color=darkgreen>// ищем свободный столбец j0 </font> | <font color=darkgreen>// ищем свободный столбец j0 </font> | ||
'''while''' true | '''while''' true | ||
Строка 125: | Строка 122: | ||
'''break''' | '''break''' | ||
− | == | + | == См. также == |
+ | |||
+ | == Источники информации == | ||
+ | * Асанов М., Баранский В., Расин В. — Дискретная математика: Графы, матроиды, алгоритмы — 2010, 368 стр. | ||
* [http://ru.wikipedia.org/wiki/Венгерский_алгоритм Венгерский алготитм в Википедии] | * [http://ru.wikipedia.org/wiki/Венгерский_алгоритм Венгерский алготитм в Википедии] | ||
* [http://rain.ifmo.ru/cat/view.php/vis/graph-flow-match/hungarian-2002 Визуализатор алгоритма] | * [http://rain.ifmo.ru/cat/view.php/vis/graph-flow-match/hungarian-2002 Визуализатор алгоритма] | ||
* [http://acm.mipt.ru/twiki/bin/view/Algorithms/HungarianAlgorithmCPP?sortcol=5&table=2&up=0 Реализация венгерского алгоритма на C++] | * [http://acm.mipt.ru/twiki/bin/view/Algorithms/HungarianAlgorithmCPP?sortcol=5&table=2&up=0 Реализация венгерского алгоритма на C++] | ||
* [http://e-maxx.ru/algo/assignment_hungary Венгерский алгоритм решения задачи о назначениях] | * [http://e-maxx.ru/algo/assignment_hungary Венгерский алгоритм решения задачи о назначениях] | ||
− | |||
− | |||
− | |||
[[Категория:Алгоритмы и структуры данных]] | [[Категория:Алгоритмы и структуры данных]] | ||
[[Категория: Задача о потоке минимальной стоимости]] | [[Категория: Задача о потоке минимальной стоимости]] |
Версия 22:54, 25 января 2016
Венгерский алгоритм (англ. Hungarian algorithm) — алгоритм, решающий задачу о назначениях за полиномиальное время. Оригинальная версия была придумана и разработана Х. Куном в 1955 году и имела асимптотику
, но позже Эдмонс и Карп (а также, независимо от них, Томидзава) показали, что можно улучшить ее до .Задача: |
Пусть дан взвешенный полный двудольный граф c целыми весами ребер , нужно найти в нем полное паросочетание минимального веса. Вес паросочетания определяется как сумма весов его ребер. Далее будем обозначать левую и правую доли графа за и соответственно, вес ребра — как . |
Содержание
Вспомогательные леммы
Лемма: |
Если веса всех ребер графа, инцидентных какой-либо вершине, изменить (увеличить или уменьшить) на одно и то же число, то в новом графе оптимальное паросочетание будет состоять из тех же ребер, что и в старом. |
Доказательство: |
Полное паросочетание для каждой вершины содержит ровно одно ребро, инцидентное этой вершине. Указанная операция изменит на одно и то же число вес любого паросочетания. Значит, ребро, которое принадлежало оптимальному паросочетанию в старом графе, в новом графе тоже будет ему принадлежать. |
Далее будем рассматривать только графы с неотрицательной весовой функцией, так как, согласно этой лемме, задачу о назначениях на остальных графах можно свести к задаче о назначениях на них.
Лемма: | |||||||||
Выделим в множествах и подмножества . Пусть . Прибавим ко всем весам ребер, инцидентных вершинам из . Затем отнимем от всех весов ребер, инцидентных вершинам из (далее для краткости эта операция обозначается как ). Тогда:
| |||||||||
Доказательство: | |||||||||
Рассмотрим матрицу весов графа. Не умаляя общности, можно сказать, что множества и состоят из первых элементов множеств и соответственно (мы упорядочиваем множества по номерам вершин). Тогда вся матрица делится на 4 блока: | |||||||||
Лемма: |
Если веса всех ребер графа неотрицательны и некоторое полное паросочетание состоит из ребер нулевого веса, то оно является оптимальным. |
Доказательство: |
Действительно, паросочетание с какими-то другими весами ребер имеет больший вес и оптимальным не является. |
Общий метод
Доказанные ранее утверждения позволяют придумать схему алгоритма, решающего задачу о назначениях: нужно найти полное паросочетание из ребер нулевого веса в графе, полученном из исходного преобразованиями, описанными в первых двух леммах.
Алгоритм, решающий задачу, работает с графом, как с матрицей весов.
- Вычитаем из каждой строки значение ее минимального элемента. Теперь в каждой строке есть хотя бы один нулевой элемент.
- Вычитаем из каждого столбца значение его минимального элемента. Теперь в каждом столбце есть хотя бы один нулевой элемент.
- Ищем в текущем графе полное паросочетание из ребер нулевого веса:
-
- Если оно найдено, то желаемый результат достигнут, алгоритм закончен.
- В противном случае, покроем нули матрицы весов минимальным количеством строк и столбцов (это не что иное, как нахождение минимального вершинного покрытия в двудольном графе). Пусть и — множества вершин минимального вершинного покрытия из левой и правой долей (то есть, строк и столбцов) соответственно, тогда применим преобразование . Для этого преобразования будет минимумом по всем ребрам между и , то есть, ребер нулевого веса здесь нет, поэтому, после его выполнения в матрице весов появится новый нуль. После этого перейдем к шагу 1.
Анализ времени работы
Поиск максимального паросочетания или минимального вершинного покрытия в двудольном графе совершается за операций. При каждом повторении шагов 1-4 в матрице весов появляется новый нуль. Этот нуль соответствует некоторому новому ребру между вершинами из множеств и . Всего в графе n^2 ребер, значит, всего будет совершено не более итераций внешнего цикла. Поэтому, верхняя оценка времени работы данного метода — . Более точная оценка довольно сложна и зависит от порядка чисел в матрице весов графа.
Алгоритм за
— прямоугольная входная матрица, где . Матрица хранится в 1-индексации.
— массивы потенциалов.
— массив паросочетания. Для каждого стобца он хранит номер соответствующей выбранной строки (или 0, если ничего не выбрано). Полагаем, что равно номеру рассматриваемой строки.
— массив, хранящий для каждого столбца j вспомогательные минимумы, необходимые для быстрого пересчета потенциала.
— массив, содержащий информацию о том, где эти минимумы достигаются, чтобы мы могли впоследствии восстановить function hungarianAlgorithm(a): // добавляем в рассмотрение-ую строку матрицы for i = 1 to n p[0] = i j0 = 0 заполняем массивы minv — , used — false // ищем свободный столбец j0 while true used[j0] = true i0 = p[j0] = // минимум в массиве minv // пересчитываем массив minv 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 // производим пересчет потенциалов u и v, соответствующее изменение массива minv for j = 0 to m if used[j] u[p[j]] += v[j] -= else minv[j] -= j0 = j1 if p[j0] != 0 break // ищем увеличивающую цепочку, оканчивающуюся в столбце j0, "раскрутить" которую можно, пользуясь массивом предков way while true j1 = way[j0] p[j0] = p[j1] j0 = j1 if j0 0 break
См. также
Источники информации
- Асанов М., Баранский В., Расин В. — Дискретная математика: Графы, матроиды, алгоритмы — 2010, 368 стр.
- Венгерский алготитм в Википедии
- Визуализатор алгоритма
- Реализация венгерского алгоритма на C++
- Венгерский алгоритм решения задачи о назначениях