Loading AI tools
Из Википедии, свободной энциклопедии
Венгерский алгоритм — алгоритм оптимизации, решающий задачу о назначениях за полиномиальное время (см. исследование операций). Он был разработан и опубликован Гарольдом Куном в 1955 году. Автор дал ему имя «венгерский метод» в связи с тем, что алгоритм в значительной степени основан на более ранних работах двух венгерских математиков (Кёнига и Эгервари[англ.]).
Джеймс Манкрес[англ.] в 1957 году заметил, что алгоритм является (строго) полиномиальным. С этого времени алгоритм известен также как алгоритм Куна — Манкреса или алгоритм Манкреса решения задачи о назначениях. Временная сложность оригинального алгоритма была , однако Эдмондс[англ.] и Карп (а также Томидзава независимо от них) показали, что его можно модифицировать так, чтобы достичь времени выполнения . Модифицированный венгерский алгоритм получил название алгоритм Хопкрофта-Карпа. Форд и Фалкерсон распространили метод на общие транспортные задачи. В 2006 году было обнаружено, что Якоби нашёл решение задачи о назначениях в XIX веке, которое было опубликовано на латыни в посмертном сборнике трудов 1890 года.[1][2]
Предположим, есть три работника: Иван, Пётр и Андрей. Нужно распределить между ними выполнение трёх видов работ (которые мы назовём A, B, C), каждый работник должен выполнять только одну разновидность работ. Как это сделать так, чтобы потратить наименьшую сумму денег на оплату труда рабочих? Сначала необходимо построить матрицу стоимостей работ.
A | B | C | |
---|---|---|---|
Иван | 10.000 руб. | 20.000 руб. | 30.000 руб. |
Пётр | 30.000 руб. | 30.000 руб. | 30.000 руб. |
Андрей | 30.000 руб. | 30.000 руб. | 20.000 руб. |
Венгерский алгоритм, применённый к приведённой выше таблице, даст нам требуемое распределение: Иван выполняет работу A, Пётр — работу B, Андрей — работу С.
Дана неотрицательная матрица размера n×n, где элемент в i-й строке и j-м столбце соответствует стоимости выполнения j-го вида работ i-м работником. Нужно найти такое соответствие работ работникам, чтобы расходы на оплату труда были наименьшими. Если цель состоит в нахождении назначения с наибольшей стоимостью, то решение сводится к решению только что сформулированной задачи путём замены каждой стоимости C на разность между максимальной стоимостью и C.[3]
Алгоритм основан на двух идеях:
Алгоритм ищет значения, которые надо вычесть из всех элементов каждой строки и каждого столбца (разные для разных строк и столбцов), такие, что все элементы матрицы останутся неотрицательными, но появится нулевое решение.
Алгоритм проще описать, если сформулировать задачу, используя двудольный граф. Дан полный двудольный граф G=(S, T; E) c n вершинами, соответствующими работникам (S), и n вершинами, соответствующими видам работ (T); стоимость каждого ребра c(i, j) неотрицательна. Требуется найти совершенное, или полное паросочетание c наименьшей стоимостью.
Будем называть функцию потенциалом, если для каждых . Значение потенциала определяется как . Нетрудно заметить, что стоимость любого совершенного паросочетания не меньше, чем значение любого потенциала. Венгерский метод находит полное паросочетание и потенциал с одинаковой стоимостью/значением, что доказывает оптимальность обеих величин. Фактически он находит совершенное паросочетание жёстких рёбер: ребро называется жёстким для потенциала , если . Подграф жёстких рёбер будем обозначать как . Стоимость полного паросочетания в (если оно существует) равна значению .
Алгоритм хранит в памяти потенциал и ориентацию (задание направления) каждого жёсткого ребра, обладающую тем свойством, что рёбра, направленные от к образуют паросочетание, которое мы обозначим . Ориентированный граф, состоящий из жёстких рёбер с заданной ориентацией, мы обозначаем . Таким образом, в любой момент есть три типа рёбер:
Изначально везде равно 0, и все рёбра направлены от к (таким образом, пусто). На каждом шаге или модифицируется так, что увеличивается множество вершин , определённое в следующем абзаце, или изменяется ориентация, чтобы получить паросочетание с большим числом рёбер; при этом всегда остаётся верным, что все рёбра из являются жёсткими. Процесс заканчивается, если — совершенное паросочетание.
Пусть на каждом шаге и составляют множество вершин, не инцидентных рёбрам из (то есть — множество вершин из , в которые не входит ни одно ребро, а — множество вершин из , из которых не исходит ни одно ребро). Обозначим через множество вершин, достижимых из в (оно может быть найдено поиском в ширину).
Если не является пустым, это значит, что есть хотя бы один путь в из в . Выбираем любой из таких путей, и изменяем ориентацию всех его ребёр на обратную. Размер паросочетания увеличится на 1.
Для доказательства отметим два очевидных факта:
- На выбранном пути рёбра из в чередуются с рёбрами из в . Это следует из двудольности графа.
- Первая вершина пути принадлежит , а последняя — . Следовательно, первое и последнее его рёбра направлены из в .
По определению , отсюда следует, что на выбранном пути рёбра, принадлежащие и не принадлежащие чередуются, причём первое и последнее рёбра не принадлежат , то есть путь является повышающим, откуда и следует доказываемое утверждение.
Если пусто, положим . положительна, потому что нет жёстких рёбер между и .
В самом деле, пускай такое ребро (i, j) есть. Поскольку , но , вершина достижима из в , а вершина недостижима.
Следовательно, не может содержать ребра (i, j). Следовательно, , откуда . Поскольку достижима из в , существует путь в из какой-то вершины, принадлежащей . Рассмотрим последнее ребро этого пути. Обозначим его (k, i). Поскольку достижима из в , а недостижима, . Поскольку , . Следовательно, инцидентна сразу двум рёбрам из : и , что невозможно, так как — паросочетание. Противоречие.
Увеличим на на вершинах из и уменьшим на на вершинах, входящих в . Новый остаётся потенциалом.
В самом деле, для любого ребра (i, j), , :
- если , , то c(i, j)-y(i)-y(j) не меняется, потому что не меняются ни y(i), ни y(j)
- если , , то c(i, j)-y(i)-y(j) не меняется, потому что y(i) увеличивается на , а y(j) на столько же уменьшается
- если , , разность c(i, j)-y(i)-y(j) увеличивается на , следовательно, остаётся неотрицательной
- если , , разность c(i, j)-y(i)-y(j) уменьшается на , но всё равно остаётся неотрицательной, потому что — наименьшая из таких разностей.
Граф меняется, но, несмотря на это, содержит .
В самом деле, чтобы исключить из некое ребро (i, j), , , надо сделать его нежёстким, то есть повысить разность c(i, j)-y(i)-y(j). Как мы видели, разность повышается только если , , иными словами, недостижима из , а достижима. Но в таком случае ребро (j, i) не может принадлежать , следовательно, ребро не принадлежит .
Ориентируем новые рёбра от к . По определению , множество вершин, достижимых из , увеличится (при этом число жёстких рёбер вовсе не обязательно возрастёт).
Для доказательства этого утверждения сначала докажем, что ни одна вершина не пропадёт из Z. Пусть . Тогда существует путь из некоей вершины, принадлежащей , в V. Все вершины на этом пути достижимы из , то есть принадлежат Z. Каждое рёбро на этом пути инцидентно двум вершинам из Z. Как мы видели выше, для таких рёбер разность c(i, j)-y(i)-y(j) не меняется. Значит, все рёбра пути останутся жёсткими, и V по-прежнему будет достижима из . Теперь докажем, что хотя бы одна вершина добавится к Z. Рассмотрим ребро, на котором достигается минимум . Для этого ребра, разность c(i, j)-y(i)-y(j) обнулится, следовательно, оно станет жёстким и будет направлено из S в T, то есть от i к j. Поскольку , j также станет достижимым из , то есть добавится к Z.
Повторяем эти шаги до тех пор, пока не станет совершенным паросочетанием; в этом случае оно даёт назначение с наименьшей стоимостью. Время выполнения этой версии алгоритма равно : дополняется раз, а в стадии, когда не меняется, может быть не более изменений потенциала (так как увеличивается каждый раз). Время, необходимое на изменение потенциала, равно .
Для работников и работ, дана матрица n×n, задающая стоимость выполнения каждой работы каждым работником. Найти минимальную стоимость выполнения работ, такую что каждый работник выполняет ровно одну работу, а каждую работу выполняет ровно один работник.
В дальнейшем мы под назначением понимаем соответствие между работниками и работами, имеющее нулевую стоимость, после того как мы произвели трансформации, влияющие лишь на общую стоимость работ.
Прежде всего запишем задачу в матричной форме:
где a, b, c, d — работники, которые должны выполнить работы 1, 2, 3, 4. Коэффициенты a1, a2, a3, a4 обозначают стоимость выполнения работником «a» работ 1, 2, 3, 4 соответственно. Аналогичный смысл имеют остальные символы. Матрица квадратная, поэтому каждый работник может выполнить только одну работу.
Шаг 1
Уменьшаем элементы построчно. Находим наименьший из элементов первой строки (а1, а2, а3, а4), и вычитаем его из всех элементов первой строки. При этом хотя бы один из элементов первой строки обнулится. То же самое выполняем и для всех остальных строк. Теперь в каждой строке матрицы есть хотя бы один ноль. Иногда нулей уже достаточно, чтобы найти назначение. Пример показан в таблице. Красные нули обозначают назначенные работы.
0 | a2' | 0 | a4' |
b1' | b2' | b3' | 0 |
0 | c2' | c3' | c4' |
d1' | 0 | d3' | d4' |
При большом количестве нулей для поиска назначения (нулевой стоимости) можно использовать алгоритм нахождения максимального паросочетания двудольных графов, например алгоритм Хопкрофта — Карпа. Кроме того, если хотя бы в одном столбце нет нулевых элементов, то назначение невозможно.
Шаг 2
Часто на первом шаге нет назначения, как, например, в следующем случае:
0 | a2' | a3' | a4' |
b1' | b2' | b3' | 0 |
0 | c2' | c3' | c4' |
d1' | 0 | d3' | d4' |
Задача 1 может быть эффективно (за нулевую стоимость) выполнена как работником a, так и работником c, зато задача 3 не может быть эффективно выполнена никем.
В таких случаях мы повторяем шаг 1 для столбцов и вновь проверяем, возможно ли назначение.
Шаг 3
Во многих случаях мы достигнем желаемого результата уже после шага 2. Но иногда это не так, например:
0 | a2' | a3' | a4' |
b1' | b2' | b3' | 0 |
0 | c2' | c3' | c4' |
d1' | 0 | 0 | d4' |
Если работник d выполняет работу 2, некому выполнять работу 3, и наоборот.
В таких случаях мы выполняем процедуру, описанную ниже.
Сначала, используя любой алгоритм поиска максимального паросочетания в двудольном графе, назначаем как можно больше работ тем работникам, которые могут их выполнить за нулевую стоимость. Пример показан в таблице, назначенные работы выделены красным.
0 | a2' | a3' | a4' |
b1' | b2' | b3' | 0 |
0 | c2' | c3' | c4' |
d1' | 0 | 0 | d4' |
Отметим все строки без назначений (строка 1). Отметим все столбцы с нулями в этих строках (столбец 1). Отметим все строки с «красными» нулями в этих столбцах (строка 3). Продолжаем, пока новые строки и столбцы не перестали отмечаться.
× | ||||
0 | a2' | a3' | a4' | × |
b1' | b2' | b3' | 0 | |
0 | c2' | c3' | c4' | × |
d1' | 0 | 0 | d4' |
Теперь проводим линии через все отмеченные столбцы и неотмеченные строки.
× | ||||
0 | a2' | a3' | a4' | × |
b1' | b2' | b3' | 0 | |
0 | c2' | c3' | c4' | × |
d1' | 0 | 0 | d4' |
Все эти действия преследовали лишь одну цель: провести наименьшее количество линий (вертикалей и горизонталей) так, чтобы покрыть все нули. Можно было воспользоваться любым другим методом вместо описанного.
Шаг 4
Из непокрытых линиями элементов матрицы (в данном случае это a2', a3', a4', c2', c3', c4') найти наименьший. Вычесть его из всех не отмеченных строк и прибавить ко всем пересечениям отмеченных строк и столбцов. Так, например, если наименьший элемент из перечисленных равен а2', мы получим
× | ||||
0 | 0 | a3'-а2' | a4'-a2' | × |
b1'+a2' | b2' | b3' | 0 | |
0 | c2'-а2' | c3'-а2' | c4'-а2' | × |
d1'+a2' | 0 | 0 | d4' |
Повторять процедуру (шаги 1-4) до тех пор, пока назначение не станет возможным.
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.