da Esame di Programmazione Luglio 2005 TESTO Esercizio 2 (punti 20 in prima approssimazione) Si tratta di dettagliare a livello di pseudo codice un algoritmo ad alto livello per il problema di trovare un abbinamento stabile (stable matching). Questo problema è interessante perchè la sua soluzione costituisce la base per la soluzione di problemi quali: il miglior modo di gestire l'abbinamento studenti-aziende per tirocini, oppure abbinare persone che cercano lavoro ed aziende che cercano personale,.... Il problema in forma stilizzata. Abbiamo un insieme U = {u1 , u2 , ...,un} di uomini e D= {d1 , d2 , ...,dn} di donne; notare che gli insiemi hanno lo stesso numero di elementi. Cascun uomo ha una sua graduatoria per D (graduatoria, in ordine di preferenza, completa, nel senso che copre tutto D, e senza ex aequo); analogamente ogni donna ha una sua graduatoria per U. Un abbinamento è un insieme A di coppie di UxD, tale che ogni u di U ed ogni w di W compare una ed una sola volta (in altre parole, A è una funzione bigettiva da U in D). Dato un abbinamento A, se A contiene due coppie (u, d) e (u', d') tali che u preferisce d' e d' preferisce u (cioè: d' precede d nella graduatoria di u e u precede u' nella graduatoria di d' ) allora abbiamo una instabilità: è ragionevole che u e d' abbandonino il partner e formino una nuova coppia. Quello che si vuole è un abbinamento che non contenga instabilità, detto abbinamento stabile. Con le condizioni dette sopra su U, D e le graduatorie, il problema ammette soluzione sempre ed esistono algoritmi per trovarne una. L'algoritmo che vediamo è il più antico (anni 60) ed ha taglio e risultati maschilisti ... Algoritmo di Gale-Shapley • All'inizio tutti gli u in U sono liberi e tutte le d in D sono libere. • Ciclo: se ci sono uomini liberi allora si sceglie un uomo libero, sia u sia d la donna che è prima nella graduatoria di u tra quelle a cui u non ha ancora fatto proposte se d è libera allora si forma la coppia (u, d) altrimenti d è già in coppia con u' : se d preferisce u' ad u, allora u rimane libero e d resta con u'; se invece d preferisce u, allora u' diventa libero e si forma la nuova coppia (u, d) • All'uscita dal ciclo, l'insieme delle coppie correnti fornisce un abbinamento stabile 1 Scopo dell'esercizio è specificare meglio l'algoritmo: • precisando le strutture dati necessarie per memorizzare U, D, le graduatorie, le coppie già formate e quant'altro serve; • precisando come si sceglie u, come si trova d eccetera L'algoritmo che dovete scrivere voi deve essere strutturato come segue: • • • da un file DATI, si leggono i dati (cioè: U, D, e le graduatorie) e si memorizzano; ciclo ........ ; nel ciclo, una volta scelto u, tutto il resto deve essere fatto tramite una o piu' procedure/funzioni; all'uscita del ciclo, si stampano, in qualche ordine, le coppie dell'abbinamento ottenuto. Non si possono usare "variabili globali". I passaggi di dati tra main e procedure e funzioni devono avvenire tramite parametri e il risultato delle funzioni. Si possono usare procedure / funzioni ausiliarie. L'enfasi è sulla struttura piu' che sui dettagli ! Non perdere tempo a dettagliare: apertura file, formato nelle istruzioni di input / output, .... ed altri aspetti di poco conto. Domande a) Come si rappresentano i dati nel file ? Se non trovo la risposta non correggo il resto. b) Precisare le strutture dati principali usate nell'algoritmo. Se non trovo la risposta non correggo il resto. ? c) Scrivere, usando lo pseudo-codice utilizzato nelle dispense, oppure il C, l'algoritmo completo, commentando, quando necessario. 2 SOLUZIONE Parte a) Per i dati scelgo la codifica piu' compatta, quindi uomini e donne sono rappresentati da numeri da 1 a n Formato del file DATI: 1a riga : il numero n le n righe seguenti si riferiscono, nell'ordine, a u1 , u2 , ...,un ; ciascuna di queste righe è una successione di numeri, che rappresentano la graduatoria; ad esempio con n=3 2a riga 3 1 2 3a riga 3 2 1 4a riga 2 3 1 quindi la graduatoria di u1 è d3 , d1 , d2 le n righe seguenti si riferiscono alle donne e sono analoghe Suppongo che l'input rispetti le regole. Parte b) type dando direttamente le dichiarazioni di tipo e commentandole Uomo = record end grad : array[ 1 .. n ] of integer prima : integer partner : integer (i valori vanno da 1 a n) (vedi sotto) (vedi sotto) prima e` un indice che "punta" nell'array grad; all'inizio prima = 1, man mano che l'uomo fa proposte, l'indice viene incrementato ... partner: se è 0, indica "libero", altrimenti contiene il partner corrente type Donna = record end grad : array[ 1 .. n ] of integer partner : integer qui non serve un campo Inoltre uso (i valori vanno da 1 a n) (come sopra) prima U : array [1 .. n] of Uomo D : array [1 .. n] of Donna dove la posizione individua la persona: quindi U[1] è il record di u1 ........... Per quanto riguarda l'accoppiamento corrente, cioè l'insieme delle coppie già formate: l'informazione è contenuta nell'array U (ed anche in D); quindi, alla fine basta scorrere uno dei due array per produrre l'output. 3 Parte c) Algoritmo in pseudo codice qui dettaglio tutto, ma non era richiesto ..... var n : integer DATI : File leggi n dal file DATI { blocco con dichiarazioni: i tipi Uomo e Donna, i due array U e D la procedura cerca (vedi sotto) variabili ausiliarie : k, j , finito,.... inizializzazioni e letture: per k = 1, 2, ..., n : { U[k].partner <--- 0 ; U[k].prima <--- 1; D[k].partner <--- 0 } per k = 1, 2, ..., n : per j = 1, 2, ...n : leggi da DATI U[k].grad[j] per k = 1, 2, ..., n : per j = 1, 2, ...n : leggi da DATI D[k].grad[j] ciclo: finito <--- falso while (not finito) do { scelgo il primo uomo libero partendo dall'alto per k = 1, 2, ..., n : if U[k].partner = 0 then break if k > n then finito <--- vero else } cerca (U, D, k) fine del ciclo per k = 1, 2, ...,n : } vedi sotto ora leggendo i partner degli uomini ho il risultato: scrivi ("coppia : uomo :" , k, " donna : " , U[k].partner, "a capo") fine del blocco e del programma 4 procedura cerca ( UU : array [1 .. n] of Uomo IN-OUT DD : array [1 .. n] of Donna IN-OUT kk : integer IN { var valore compreso tra 1 ed n ) jj , uu, prima : integer prima <--- UU[kk].prima jj <--- UU[kk].grad [prima] uu <--- DD[jj].partner if uu = 0 then { DD[jj].partner <--- kk ; UU[kk]. partner <--- jj } else { per ii = 1, 2, ..., n : if DD[jj].grad[ii] = uu or DD[jj].grad[ii] = kk then break notare che senza dubbio trovero` uno o l'altro .... if DD[jj].grad[ii] = kk then { cioè lei preferisce il nuovo arrivato DD[jj].partner <--- kk ; UU[kk]. partner <--- jj UU[uu].partner <--- 0 } UU[kk].prima ++ } 5