edutecnica

Esercizio 8                     

Dato un insieme A di n uomini e un insieme B di n donne, assegnare ciascuno degli n uomini a ciascuna delle donne in modo che ogni soluzione sia priva di instabiltà; un matrimonio viene ritenuto stabile se ciascun uomo che preferisce un'altra compagna a quella che gli ha assegnato l'algoritmo è già stato rifiutato precedentemente da ella (che evidentemente gli preferisce un altro).
Utilizzare una LinkedList per tenere traccia degli uomini non sposati.


L'algoritmo dei matrimoni stabili è un classico dei problemi di assegnazione: siano A e B due insiemi dotati di uguale cardinalità n, si trovi un insieme di coppie < a,b > tali che a in A e b in B soddisfino una condizione data. Si possono stabilire vari criteri di selezione; uno di questi viene denominato la 'regola del matrimonio stabile'.

Si assume cha A sia un insieme di n uomini e B un insieme di n donne e che tutti abbiano dichiarato le proprie preferenze in materia di compagni di coppia. Se le n coppie vengono assegnate in modo tale che esistono un uomo e una donna che si preferiscono reciprocamente al posto dei propri promessi sposi allora si dice che gli assegnamenti dati sono instabili.
Se tale coppia non esiste, allora gli assegnamenti sono detti stabili.

Si tratta di un algoritmo di scelta ottima e come nel caso dell'algoritmo delle otto regine può essere risolto con tecniche di backtracking (procedendo a tentativi).
Si nota come possa essere applicato a diversi problemi analoghi ( di tipo associativo ) come ad esempio la scelta di un ospedale da parte di un medico, oppure la scelta di una scuola da parte degli studenti oppure la scelta delle reclute da parte di diverse forze armate, ma come già detto va applicato fra due insiemi costituiti ciascuno da n elementi.

Una possibile strategia consiste nel costruire iterativamente le coppie, determinando per ogni uomo la sua compagna grazie ad un sistema di offerte successive: l'uomo chiede di associarsi alla donna che preferisce , ma se questa gli preferisce il compagno attribuitole durante i passi precedenti dall'algoritmo, l'uomo la cancella dalla sua lista e si propone a quella successiva; se invece la donna preferisce lui al proprio compagno attuale (oppure se è ancora nubile) l'uomo le viene ( almeno provvisoriamente ) assegnato e l'eventuale compagno che lei ha rifiutato torna in circolazione per cercare la successiva donna della sua lista e il giro avrà termine appena si arriva ad una donna sola oppure a una donna che lo preferisce rispetto al suo attuale compagno.

Chiaramente, servono delle strutture di dati per definire, almeno all'inizio, le preferenze dei vari soggetti.
Per avere una idea, supponiamo di avere n=5 con gli uomini rappresentati da una lettera inscritta in un quadrato e le donne rappresentate da dei numeri inscritti in un cerchio:


Da cui si capisce che l'uomo A preferisce in ordine la donna 2 poi la 5, la 1 ..etc. La donna 1 preferisce in ordine discendente l'uomo E poi A,D,B ed infine C.
Un set di matrimoni è instabile se due soggetti non sposati preferiscono entrambi altri soggetti ai loro promessi sposi. Si vede, inoltre come le assegnazioni 1A 3B 2C 4D 5E sono instabili, perché A preferisce 2 a 1 e 2 preferisce A a C; succederebbe che A lascia 1 per 2 e 2 lascia C per A.

Se l'algoritmo funzionasse dovrebbero accadere i seguenti eventi:


I primi due passaggi sono privi di problemi perché ciascun uomo sceglierebbe la donna che gli interessa; quando si inserisce C, esso tenterà di associarsi alla donna 2 in quel momento accompagnata all'elemento A, ma non riesce a farlo perché nella lista delle preferenze della 2, A risulta preferibile a C, quindi C sceglierà l'elemento successivo nella sua lista cioè la donna 3 in quel momento libera.
Al quarto passaggio si inserisce l'elemento D che può associarsi alla donna 1 che precedentemente stava con B: può farlo perché nella lista di 1, D ha la priorità su B. B ritorna dunque libero puntando alla donna 2 (che prima stava con A) che nella sua lista viene immediatamente dopo 1, riesce in questa operazione perché nella lista di 2 B ha priorità su A??.in pratica si va aventi secondo questa logica; alla fine avremo se seguenti associazioni:

1+A
4+B
5+C
3+D
2+E

per ragioni di praticità saremo costretti ad usare due matrici quadrate di interi indicizzate da 0 a n-1 dove
i=0≡A≡1
i=1≡B≡2
i=2≡C≡3
i=3≡D≡4
i=4≡E≡5

quindi l'elenco finale dei matrimoni sarebbe
D+U
0+0
1+4
2+3
3+2
4+1

I dati precedenti verrebbero codificati in due matrici
int pref[][]={//tabella preferenze degli uomini
   {1,4,0,2,3},
   {0,1,2,3,4},
   {1,2,4,3,0},
   {0,2,1,3,4},
   {4,2,1,0,3}
};
int rango[][]={//tabella preferenze delle donne
   {4,0,3,1,2},
   {3,4,1,0,2},
   {0,3,1,2,4},
   {2,1,3,0,4},
   {3,1,2,4,0}
};
vengono dichiarate le variabili
int i,u=0,d=0,n=5;
i=contatore u rappresenta un generico uomo, d una generica donna.
n è il valore più importante: di quanti elementi sono costituiti gli insiemi.

Poi,per ogni donna, dobbiamo sapere chi è il suo attuale fidanzato:.
int fida[]= new int[n];
cioè fida[d] ci indica l'uomo u fidanzato alla donna d.
Tutti gli elementi di questo vettore vengono inizializzati a -1 per specificare che inizialmente nessuna donna è accompagnata.

Poi per sapere quali uomini non sono attualmente impegnati usiamo una linkedlist di interi .
LinkedList < Integer > lista = new LinkedList < Integer > ();
essa viene inizializzata con gli indici identificativi degli uomini.
Inoltre, abbiamo bisogno di tenere traccia di quanto ogni uomo è avanzato sulla sua lista. Questo può essere considerato attraverso il vettore next[ ].
next[u] indica la donna attuale, next[u]++ è l'indice della prossima donna nella lista di preferenze dell'uomo u.
Il ciclo while deve continuare finchè nella lista ci sono uomini non accompagnati; esso è costituito da due parti; nella prima si valuta se la donna che arriva per prima nell'elenco non è accompagnata; se non lo è viene assegnata all'uomo attuale. Nella seconda si chiama il metodo statico scelta per valutare se il rango dell'uomo considerato può scavalcare il rango dell'uomo attualmente accompagnato alla donna d. Se questo avviene si deve procedere ad uno scambio attraverso la variabile ausiliaria t, mentre torneranno nella lista degli uomini liberi o il soggetto attuale .
lista.add(u);
o il suo ex fidanzato:
fida[d] = u;
lista.add(t);
alla fine di tutte queste iterazioni (potrebbero essere parecchie) quando gli elementi della LinkedList si sono esauriti, si stampa la configurazione finale attraverso il metodo
stampaMat(fida);
Ovviamente, la soluzione ottenuta è priva di instabilità: se un uomo alla compagna che gli ha assegnato l'algoritmo preferisce un'altra donna, ma da questa, egli è già stato rifiutato.


import java.util.LinkedList;
class stabile{
public static void main (String[] args) {
final int NA = -1;//non accompagnato
int i,u=0,d=0,n=5;
//tabella preferenze degli uomini
int pref[][]={{1,4,0,2,3},{0,1,2,3,4},{1,2,4,3,0},{0,2,1,3,4},
{4,2,1,0,3}};
//tabella preferenze delle donne
int rango[][]={{4,0,3,1,2},{3,4,1,0,2},{0,3,1,2,4},{2,1,3,0,4},{3,1,2,4,0}};
int fida[]= new int[n];
for(i=0;i < fida.length; i++)fida[i]=NA;
//Elenco di uomini non attualmente impegnati.
LinkedList < Integer > lista = new LinkedList < Integer >();
for(i=0;i< fida.length;i++)lista.add(i);
int next[]= new int[n];
//next[i] è la prossima donna nella lista delle pref.
while (!lista.isEmpty()) {
u = lista.remove();
d = pref[u][next[u]];
next[u]++;
if (fida[d]==NA) {//-
   fida[d] = u;
}else{
   int t = fida[d];
   if (scelta(d,u,t,n,rango)) {//--
       fida[d] = u; lista.add(t);
   }else{
      lista.add(u);
   }//--
}//-
}//fine while
stampaMat(fida);
}//fine main

static boolean scelta(int d, int x, int y, int n,int rango[][]) {
for(int i=0;i < n;i++){
   int j = rango[d][i];
   if(j==x)return true;
   if(j==y)return false;
}//fine for
//se c'è un possibile dato duplicato
System.out.println("errore nella tabella rango:" + d);
return false; }//fine scelta

static void stampaMat(int T[]) {
System.out.println("D + U");
for (int i=0; i < T.length; i++)
System.out.println(i + " + " + T[i]);
}//fine stampaMat
}//fine classe


E' possibile fare una variante del programma precedente, permettendo l'inserimento della dimensione n ( 1< n< 10 ) dell'insieme e dove le matrici delle preferenze vengono caricate con valori casuali.

import java.util.LinkedList;
import java.util.Scanner;
class stabile{ public static void main (String[] args) {
Scanner in=new Scanner(System.in);
final int NA = -1;
int i,u=0,d=0,n;
do{
   System.out.print("n:");
   n=in.nextInt();
}while(n < 2 || n > 9);
int pref[][]=new int[n][n];//tabella preferenze degli uomini
int rango[][]=new int[n][n];//tabella preferenze delle donne
int fida[]= new int[n];

for(i=0;i < fida.length; i++)fida[i]=NA;
LinkedList < Integer > lista = new LinkedList < Integer > ();
for(i=0;i < fida.length;i++)lista.add(i);
int next[]= new int[n];
load(pref);//carico le matrici
load(rango);

while (!lista.isEmpty()) {
u = lista.remove();
d = pref[u][next[u]];
next[u]++;
if (fida[d]==NA) {//-
   fida[d] = u;
}else{
   int t = fida[d];
   if (scelta(d,u,t,n,rango)) {//--
      fida[d] = u;
      lista.add(t);
   }else{
      lista.add(u);
   }//--
}//-
}//fine while
System.out.println("preferenze degli uomini"); print(pref);
System.out.println("preferenze delle donne"); print(rango);
stampaMat(fida);
}//fine main

static boolean scelta(int d, int x, int y, int n,int rango[][]) {
for(int i=0;i < n;i++){
   int j = rango[d][i];
   if(j==x)return true;
   if(j==y)return false;
}//fine for
System.out.println("errore nella tabella rango:" + d);
return false;
}//fine scelta

static void stampaMat(int T[]) {
System.out.println("\nD + U");
for (int i=0; i < T.length; i++)
System.out.println(i + " + " + T[i]);
}//fine stampaMat

static void load(int T[][]){
boolean d=false; int i,j,h; //caricamento
for(i=0;i < T.length;i++)
   for(j=0;j < T[0].length;j++)       if(j==0)T[i][j]=(int)(Math.random()*T[0].length);
      else do{
         d=false;
         T[i][j]=(int)(Math.random()*T[0].length);
         for(h=0;h < j;h++)
         if(T[i][j]==T[i][h])
         d=true;
         }while(d==true);
}//fine load

static void print(int T[][]){//stampa le matrici pref e rango
int i,j;
for(i=0;i < T.length;i++){
   for(j=0;j < T[0].length;j++)System.out.print(T[i][j]+" ");
   System.out.println();
   }//fine for i
}//fine print
}//fine classe