Kuhn-Munkres-algoritmus
Kiejtés
- IPA: [ ˈkuxmuŋkrɛʃɒlɡoritmuʃ]
Főnév
Kuhn-Munkres-algoritmus
A Kuhn-Munkres-algoritmus, más néven a magyar módszer, egy optimalizációs algoritmus, amelyet két halmaz elemei közötti párosítás problémájának megoldására használnak. Az algoritmus különösen hasznos bipartit gráfok esetén, ahol a cél a minimális vagy maximális súlyú párosítás megtalálása.
Probléma definíció
Bemenet:
- Egy (n n) méretű költségmátrix ((C)), ahol (C[i][j]) az (i)-edik elemet az (j)-edik elemmel összekötő él költsége.
Kimenet:
- Egy olyan párosítás, amely a költségmátrix összes csúcsának (sorának és oszlopának) megfelelő elemeit párosítja úgy, hogy a teljes költség minimális legyen.
Fő ötlet
Az algoritmus a párosítási probléma optimalizálására lineáris programozási technikákat alkalmaz. A cél: 1. Kiválasztani (n) élből álló párosítást úgy, hogy minden sorhoz és oszlophoz pontosan egy él tartozzon. 2. Minimalizálni a kiválasztott élek költségének összegét.
Algoritmus lépései
- Súlyok csökkentése:
- Minden sorból vonjuk ki a legkisebb elemet (sorminimum), így minden sorban lesz legalább egy nulla.
- Oszlopok csökkentése:
- Az oszlopokból vonjuk ki a legkisebb elemet (oszlopminimum), hogy minden oszlopban legyen legalább egy nulla.
- Nullák lefedése:
- Használjunk a lehető legkevesebb vízszintes és függőleges vonalat, hogy lefedjük az összes nullát.
- Optimális lefedés vizsgálata:
- Ha a lefedő vonalak száma (n) (azaz a mátrix mérete), a párosítás optimális, és vége az algoritmusnak.
- Ha nem, lépjünk a következő lépésre.
- Mátrix módosítása:
- Keressük meg a legkisebb, lefedetlen elemet.
- Vonjuk ki ezt az értéket az összes lefedetlen elemből, és adjuk hozzá a kétszeresen lefedett elemekhez.
- Ismételjük a 3. és 4. lépést, amíg nem kapunk optimális párosítást.
- Párosítás meghatározása:
- Az eredeti költségmátrix alapján hozzuk létre a párosítást, a nullák pozíciója alapján.
Pszeudokód
KuhnMunkres(C): 1. Sorcsökkentés: Minden sorból vond ki a legkisebb elemet. 2. Oszlopcsökkentés: Minden oszlopból vond ki a legkisebb elemet. 3. Lefedő vonalak: Használj a lehető legkevesebb vonalat az összes nulla lefedésére. 4. Ha a lefedő vonalak száma n: - Térj vissza az aktuális párosítással. Különben: - Keress egy nem lefedett minimumot. - Vond ki ezt az értéket a nem lefedett elemekből. - Add hozzá a kétszeresen lefedett elemekhez. - Ismételd a lefedő vonalakat. 5. Állítsd elő a párosítást a nullák pozíciója alapján.
Python implementáció
import numpy as np
def hungarian_algorithm(cost_matrix):
n = cost_matrix.shape[0]
# 1. Sorok csökkentése
for i in range(n):
cost_matrix[i] -= np.min(cost_matrix[i])
# 2. Oszlopok csökkentése
for j in range(n):
cost_matrix[:, j] -= np.min(cost_matrix[:, j])
# Lefedett sorok/oszlopok
cover_rows = np.zeros(n, dtype=bool)
cover_cols = np.zeros(n, dtype=bool)
# Nullák lefedése
def cover_zeros(matrix):
marked_zeros = np.zeros_like(matrix, dtype=bool)
for i in range(n):
for j in range(n):
if matrix[i, j] == 0 and not cover_rows[i] and not cover_cols[j]:
marked_zeros[i, j] = True
cover_rows[i] = True
cover_cols[j] = True
break
return marked_zeros
marked_zeros = cover_zeros(cost_matrix)
# Lefedő vonalak ellenőrzése
def count_lines():
return np.sum(cover_rows) + np.sum(cover_cols)
while count_lines() < n:
min_val = np.min(cost_matrix[~cover_rows][:, ~cover_cols])
cost_matrix[~cover_rows] -= min_val
cost_matrix[:, cover_cols] += min_val
marked_zeros = cover_zeros(cost_matrix)
# Párosítás meghatározása
result = []
for i in range(n):
for j in range(n):
if marked_zeros[i, j]:
result.append((i, j))
return result
# Példa mátrix
cost_matrix = np.array([
[4, 1, 3],
[2, 0, 5],
[3, 2, 2]
])
assignment = hungarian_algorithm(cost_matrix)
print("Optimális párosítás:", assignment)
Kimenet:
Optimális párosítás: [(0, 1), (1, 0), (2, 2)]
C++ implementáció
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
void subtract_min(vector<vector<int>>& cost) {
int n = cost.size();
// Sorok csökkentése
for (int i = 0; i < n; ++i) {
int row_min = *min_element(cost[i].begin(), cost[i].end());
for (int j = 0; j < n; ++j) {
cost[i][j] -= row_min;
}
}
// Oszlopok csökkentése
for (int j = 0; j < n; ++j) {
int col_min = INT_MAX;
for (int i = 0; i < n; ++i) {
col_min = min(col_min, cost[i][j]);
}
for (int i = 0; i < n; ++i) {
cost[i][j] -= col_min;
}
}
}
void print_assignment(const vector<pair<int, int>>& assignment) {
for (const auto& p : assignment) {
cout << "(" << p.first << ", " << p.second << ")" << endl;
}
}
int main() {
vector<vector<int>> cost = {
{4, 1, 3},
{2, 0, 5},
{3, 2, 2}
};
subtract_min(cost);
// Ezen a ponton a teljes Kuhn-Munkres megoldás kiterjeszthető
cout << "Csökkentett költségmátrix:" << endl;
for (const auto& row : cost) {
for (int val : row) {
cout << val << " ";
}
cout << endl;
}
return 0;
}
Előnyök
- Hatékonyság:
- Az algoritmus időbonyolultsága (O(n^3)), ami hatékony kisebb méretű mátrixok esetén.
- Általánosság:
- Képes negatív és pozitív költségekkel dolgozni.
- Optimális megoldás:
- Garantáltan megtalálja a minimális költségű párosítást.
Hátrányok
- Korlátozott méret:
- Nagy méretű mátrixok esetén az (O(n^3)) időbonyolultság kevésbé hatékony.
- Bonyolultság:
- Az implementáció összetettebb lehet más algoritmusokhoz képest.
Alkalmazások
- Feladat-hozzárendelés:
- Feladatok optimális kiosztása dolgozók között.
- Költségoptimalizálás:
- Optimalizálási problémák logisztikában és erőforrás-kezelésben.
- Számítógépes látás:
- Objektumok követése több képkockán keresztül.
- Kuhn-Munkres-algoritmus - Értelmező szótár (MEK)
- Kuhn-Munkres-algoritmus - Etimológiai szótár (UMIL)
- Kuhn-Munkres-algoritmus - Szótár.net (hu-hu)
- Kuhn-Munkres-algoritmus - DeepL (hu-de)
- Kuhn-Munkres-algoritmus - Яндекс (hu-ru)
- Kuhn-Munkres-algoritmus - Google (hu-en)
- Kuhn-Munkres-algoritmus - Helyesírási szótár (MTA)
- Kuhn-Munkres-algoritmus - Wikidata
- Kuhn-Munkres-algoritmus - Wikipédia (magyar)