Algoritmo ungherese

Da testwiki.
Vai alla navigazione Vai alla ricerca

In matematica, il metodo ungherese[1][2], o algoritmo ungherese, è un metodo di ottimizzazione combinatoria che risolve in tempo polinomiale il problema dell'assegnamento. Il metodo è stato sviluppato da Harold Kuhn nel 1955, anticipando i successivi metodi primali-duali[3], ed è chiamato "ungherese" in quanto basato[4][5] su lavori di Dénes König[6] e Jenő Egerváry[7]. Nel 2006 fu scoperto un lavoro di Carl Jacobi, risalente al XIX secolo, che risolve il medesimo problema[8]. La complessità dell'algoritmo era di O(n4), ma si è dimostrato che modificando leggermente l'algoritmo si può arrivare ad ottenere una complessità computazionale pari a O(n3).

Definizione del problema

Template:Vedi anche

Il problema dell'assegnamento richiede di associare coppie di elementi presi da due insiemi differenti. Tenendo conto che a ogni coppia è associato un costo, si vuole minimizzare il costo totale. Ad esempio: considerando un insieme di impiegati, un insieme di compiti da svolgere e il tempo che ciascun compito richiederebbe se svolto da uno specifico impiegato, si vuole associare ogni impiegato a un compito in modo da minimizzare il tempo totale richiesto.

Formalmente, data una matrice n×n di costi C=(cij), vogliamo trovare una permutazione ϕ di {1,,n} che minimizzi i=1nciϕ(i).

Algoritmo

Template:S sezione

Il problema è codificato tramite un grafo bipartito, in cui i vertici sono gli elementi da associare e gli archi rappresentano le possibili scelte di coppie. A ogni arco è associato un costo. Formalmente, si considera il grafo G=(U,V;E) dove U e V sono i due insiemi di elementi da associare ed E è l'insieme degli archi.

L'algoritmo inizia da una qualsiasi possibile soluzione, anche parziale, del problema e cerca di migliorarla verificando la presenza di elementi non assegnati.

A ogni iterazione, l'algoritmo tenta di trovare un percorso che aumenti il numero di elementi presenti nella soluzione. La prima iterazione inizia da un elemento di uU non assegnato. Vi sono due possibilità:

  1. vi è un arco che collega l'elemento u a un elemento vV non facente parte della soluzione
  2. vi sono solo archi che collegano l'elemento u ad elementi di V già assegnati

Nel primo caso, l'assegnamento tra u e v è aggiunto alla soluzione.

Interpretazione matriciale dell'algoritmo

Data una matrice quadrata di ordine n rappresentante la matrice dei costi del problema di assegnamento, l'algoritmo si svolge come segue

  1. Per ogni riga, individuare il minimo e sottrarlo a tutti gli elementi della riga;
  2. Per ogni colonna, individuare il minimo e sottrarlo a tutti gli elementi della colonna;
  3. Coprire con il minor numero di linee tutti gli zeri che si sono formati con le precedenti sottrazioni;
  4. Se il numero minimo di linee necessarie è pari a n è possibile determinare un assegnamento ottimo altrimenti procedere con lo step 5;
  5. Individuare il minimo tra gli elementi non coperti da linee, sottrarlo agli elementi non coperti e sommarlo agli elementi che sono incrocio di due linee. Tornare allo step 3.

Per determinare il minor numero di linee necessarie per coprire tutti gli zeri formati (step 3) si può utilizzare il seguente algoritmo:

  1. Determinare un assegnamento (anche incompleto, ovvero un assegnamento in cui può esistere qualche riga non assegnata a nessuna colonna);
  2. Marcare le righe non assegnate;
  3. Per ogni riga marcata non ancora esaminata, marcare le colonne che presentano uno zero su tale riga;
  4. Per ogni colonna non marcata, marcare le righe che presentano un assegnamento su tali colonne;
  5. Ripetere dallo step 3 fino ad esaurimento delle righe marcate non ancora visitate;
  6. Il numero minimo di linee si ottiene selezionando le colonne marcate e le righe non marcate.

Esempio

Data la seguente matrice dei costi

C0=[301893997303569386941331342472593041087255729]

I minimi di riga sono (dall'alto in basso): 9, 3, 13, 4, 10. Sottraendoli dalle rispettive righe si ottiene la seguente matrice:

C1=[219030882705360738101821206855260077154719]

I minimi di colonna sono (da sinistra a destra): 0, 0, 0, 6, 0. Sottraendoli dalle rispettive colonne si ottiene:

C2=[219024882705300738101221206855200077154119]

Si determina un assegnamento (incompleto) scorrendo le righe e assegnandole alla prima colonna possibile. Ovvero si determina il seguente assegnamento (in grassetto gli elementi assegnati):

A1=[219𝟎248827𝟎530073810122120685520𝟎𝟎77154119]

Si procede ora con la determinazione del numero minimo di linee per coprire gli elementi nulli della matrice. Viene marcata la riga 3 che non è assegnata. Tale riga presenta uno zero nella colonna 3 (che viene marcata). Tale colonna presenta un assegnamento alla riga 1 (che viene marcata). Viene esaminata ora la riga 1 (che è stata marcata nella precedente iterazione. Tale riga presenta uno zero nella colonna 3 che è già marcata. Non ci sono altre colonne marcate non esaminate e righe marcate non esaminate. Il numero minimo di linee è dato dalle colonne marcate (colonna 3) e righe non marcate (righe 2, 4, 5). Sono necessarie quindi 4<5=n linee. Non è possibile determinare un assegnamento completo e si procede con lo step 5.

Si determina il minimo tra gli elementi non coperti da linee, cioè il minimo della seguente matrice ottenuta eliminando gli elementi coperti da linee:

C^0=[219248873811221]

che è 9. Questo valore va sottratto agli elementi non coperti da linee e sommati agli elementi incrocio di due linee. Cioè si ottiene:

C3=[12001579270620064720312206864200077244119]

Si determina ora un assegnamento (eventualmente incompleto) scorrendo le righe ed assegnando la prima colonna possibile. Si ottiene

A2=[12𝟎0157927062𝟎06472𝟎31220686420𝟎𝟎77244119]

Tutte le righe sono assegnate, quindi nessuna riga è marcata. Le linee necessarie per coprire tutti gli elementi nulli è dato dalle colonne marcate (nessuna) e le righe non marcate (tutte) e sono 5=n è quindi possibile assegnare tutte le righe e l'assegnamento è dato da A2.

Note

Bibliografia

Template:Portale