Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff....

24
Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010

Transcript of Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff....

Page 1: Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010.

Risoluzione di Problemi con gli algoritmi Ricorsivi

Laboratorio di Algoritmi e Strutture Dati

Proff. Francesco Cutugno e Luigi Lamberti2009-2010

Page 2: Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010.

Problem SolvingSia assegnato un Problema (useremo anche il termine Sistema) rappresentato da :

• un insieme (finito) di variabili discrete V = {X1, X2, … Xn}

• ciascuna associata ad un dominio D1, D2, … Dn

• un insieme di vincoli (relazioni tra variabili): R = {C1, C2,… Cm}

Lo Stato è una combinazione dei valori delle variabili che definiscono il problema {Xi = vi i=1,n} ;

Lo Spazio degli Stati, insieme di tutte le possibili combinazioni dei suddetti valori, non coincide con il semplice prodotto cartesiano dei domini, per la presenza dei vincoli.

Lo Stato Iniziale è la condizione di partenza del nostro problema (in base alla formulazione fissata).

Page 3: Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010.

Un’Azione (o mossa legale) è l’assegnazione di un valore ad una variabile nel rispetto dei vincoli; la modifica di una variabile, porta il Sistema descritto da uno stato ad un altro (transizione). Il numero delle azioni possibili (input del Sistema) è limitato superiormente dalla cardinalità del prodotto dei domini.

La Soluzione (o Stato Obiettivo) è una particolare assegnazione completa delle variabili, soddisfacente tutti i vincoli; la soluzione può non essere unica.

Un Processo è la sequenza di azioni che conduce il Sistema da uno stato ad un altro; il Processo risolutivo, sarà la sequenza di azioni per giungere dallo Stato iniziale allo Stato Finale atteso.

Page 4: Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010.

Diagramma degli Stati

Stato1

A1

A2 A4

Stato2 Stato3 Stato4

A3A5

A6

A7

L’evoluzione di un Sistema è descritta da un Grafo con un Nodo per ciascuno stato e un Arco uscente per ogni azione possibile in quello stato: la destinazione indica la transizione effettuata.

Il diagramma degli stati diventa improponibile al crescere della complessità del problema.

Il grafo può contenere cicli e può essere complicato dimostrare che l'algoritmo di ricerca termina.

Page 5: Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010.

Albero di Ricerca

Stato1

Stato2 Stato3

Stato4Stato5

Stato6 Stato7

Stato5

A1 A2

A3

A4

A5 A6

A7

La gestione del problema è rappresentabile anche tramite un albero (che chiameremo Albero di Ricerca) ove la radice è lo Stato Iniziale, mentre le eventuali soluzioni sono da ricercarsi nelle foglie.

Si elimina il problema dei cicli, ma lo stesso nodo potrebbe essere generato più volte.

Page 6: Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010.

Salvare Pecora e Cavoli

Problema: un Agricoltore va al mercato con un Lupo, una Pecora e un grosso Cavolo; deve attraversare un fiume su una barca tanto piccola da poter portare un solo oggetto per volta.

Variabili: detto 0 il lato di partenza del Fiume e 1 il lato di arrivo, avremo 4 variabili, ciascuna con due possibili valori: A {0,1}, L {0,1}, P {0,1}, C {0,1}

Vincoli: Lupo, Pecora e Cavolo non sanno attraversare il fiume da soli; il Lupo mangia la Pecora, se non c’è l’Agricoltore la Pecora mangia il Cavolo, se non c’è l’Agricoltore.

Azioni: l’Agricoltore può attraversare il fiume da solo (A = 1-A), o con un passeggero A=1-A L=1-L, A=1-A P=1-P, A=1-A C=1-C, che sia dal suo stesso lato (non tutte le azioni sono legali).

Spazio Stati: Abbiamo 16 stati possibili con le combinazioni ALPC. 0000 è lo stato iniziale; 1111 è lo stato obiettivo (finale); 0110, 0011, 0111, 1001, 1100, 1000 sono stati da evitare; i restanti sono possibili stati intermedi

Page 7: Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010.

0 000

A

1 100

1 010

1 001

AL AC

1 000

0 010 1 110 0 100

0 011 1 011 0 001 1 101

0 110 0 111 0 101 1 111

AP A AL AP

AC

A

AP

AC

AP

ALA

Page 8: Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010.

Backtracking

Partendo dallo stato raggiunto, ad ogni passo si assegna

un valore ad una variabile, rispettando i vincoli,

eseguendo così una Transizione :

• Se è stato raggiunto lo stato finale, segnaliamo la

soluzione.

• Se è possibile avanzare ulteriormente,

ripetiamo l’analisi partendo dallo stato attuale.

• In caso di fallimento (vicolo cieco, violazione dei

vincoli,

ritorno ciclico ad un nodo già visitato) si torna

indietro.

• Se si ritorna allo stato iniziale senza aver trovato la

soluzione, vuol dire che il problema è irrisolubile

Supponiamo che il problema non preveda possibili

percorsi ciclici, possiamo ipotizzare il seguente algoritmo:

Page 9: Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010.

funzione Ricerca ( Stato_Attuale ){ Risultato = ‘Fallimento’

E = Elenco Azioni possibili secondo i vincoli // mosse legali

per ogni Azione A E, mentre Risultato = ‘Fallimento’ { Esegui A se Nuovo_Stato = Stato_Obiettivo Risultato = ‘Soluzione’ altrimenti

Risultato = Ricerca ( Nuovo_Stato ) se Risultato = ‘Fallimento’ Annulla esecuzione di A // backtracking

} restituisci Risultato}

Page 10: Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010.

SudokuUsiamo una griglia quadrata di 81 celle:

9 righe orizzontali per 9 colonne verticali; inoltre, la griglia è divisa in 9 riquadri 3x3 (gruppi) di 9 celle ciascuno.

Ciascuna riga, colonna e riquadro 3x3 contiene tutti i numeri da 1 a 9. Pertanto in nessuna riga, colonna o riquadro può esserci un numero ripetuto.

Nella matrice, ogni cella vale 0 se è libera, da 1 a 9 se occupata.Chiameremo Gruppi i 9 riquadri 3x3 interni. Detta (r,c) una cella della Tavola, le coordinate della cella in alto a sinistra del suo gruppo di appartenenza saranno: rg = (r / 3) * 3 = r - r % 3 cg = (c / 3) * 3 = c - c % 3

Il gruppo sarà composto dalle nove celle (i,j) con rg <= i <= rg+2 e cg <= j <= cg+2

Page 11: Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010.

/*======================================================= Esempio base per la risoluzione di un Sudoku. Manca un’interfaccia utente e il caricamento dello schema iniziale --------------------------------------------------------*/main (void){ int Tav [9][9], // Tavola di gioco Nvuote; // Numero di caselle vuote nella tavola IniziaTavola (Tav, &Nvuote); StampaTavola (Tav);

if (Nvuote<1 || !VerificaVincoli(Tav)) printf("\n Il problema e` mal posto ! \n");

else if (! CercaSudoku(Tav,Nvuote) ) printf("\n NON esistono soluzioni ! \n");

else { printf("\n Soluzione trovata. \n"); StampaTavola (Tav); }

getch();}

Page 12: Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010.

/* Esamina la tavola, con ALMENO con una cella vuota, provando a riempire una qualsiasi cella vuota, ove il valore sia possibile.

OUTPUT: 1 soluzione trovata e copiata sulla Tavola da gioco 0 nessuna soluzione possibile-----------------------------------------------------------------*/int CercaSudoku( int Tav[][9], // Tavola da esaminare int Nvuote // Numero di caselle vuote nella tavola > 0){ int r, c, // riga e colonna della cella da occupare N, // valore da inserire i, j, Trovato = 0;

r = -1; for (i=0 ; i<9 && r == -1 ; i++) // cerca una cella vuota for (j=0 ; j<9 && r == -1 ; j++) if (Tav[i][j] == 0) {r=i; c=j;} // termina se trova una vuota

for (N=9; N && !Trovato; N--) if ( VerificaLegalita(Tav, r, c, N) ) // se è possibile inserire N { Tav[r][c] = N; // se era l'ultima casella vuota, la soluzione è stata trovata Trovato = (Nvuote == 1) ? 1 : CercaSudoku (Tav, Nvuote-1); // se non è praticabile, annulla la mossa (backtracking) if (! Trovato) Tav[r][c] = 0; } return Trovato;}

Page 13: Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010.

/* Valuta la legalità di un inserimento di un nuovo valore in una casella vuota, esaminando la riga la colonna e il gruppo.

OUTPUT: 0 non è possibile inserire il valore nella cella 1 il gioco può proseguire alla ricerca di soluzioni---------------------------------------------------------------*/int VerificaLegalita( int Tav[][9], // Tavola di gioco int r, int c, // riga e colonna della cella da occupare int N // valore da inserire){ int i, j, rg, cg, // angolo del gruppo di appartenenza Congrua; // 0 = tavola inconguente

Congrua = 1; for (j=0; j<9; j++) // esamina la riga if (Tav[r][j] == N) Congrua = 0;

for (i=0; i<9; i++) // esamina la colonna if (Tav[i][c] == N) Congrua = 0;

rg = r - r % 3; // esamina il riquadro cg = c - c % 3; for (i=0; i<3; i++) for (j=0; j<3; j++) if (Tav[rg+i][cg+j] == N) Congrua = 0;

return Congrua;}

Page 14: Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010.

/*------------------------------------------------------------ Valuta la congruenza dell`intero schema di gioco

OUTPUT: 0 La tabella contiene incongruenze 1 il gioco può proseguire alla ricerca di soluzioni--------------------------------------------------------------*/int VerificaVincoli( int Tav[][9] // Tavola di gioco){ int r, c, N, Congrua = 1; // 0 = tavola inconguente for (r=0 ; r<9 ; r++) for (c=0 ; c<9 ; c++) if ( Tav[r][c] ) // esaminiamo ogni elemento gia` inserito { N = Tav[r][c]; Tav[r][c] = 0; Congrua &= VerificaLegalita (Tav, r, c, N); Tav[r][c] = N; } return Congrua;}

Page 15: Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010.

Problemi di ottimizzazioneOccorre cercare la migliore tra più soluzioni possibili, secondo una certa metrica. Il fallimento varrà -∞

funzione Ricerca ( Stato_Attuale ){ E = Elenco Azioni possibili secondo i vincoli V = elenco dei valori corrispondenti a ciascuna azione

per ogni Azione Ai E { Esegui Ai

se Nuovo_Stato = SoluzioneVi = Valore della Soluzione

altrimenti Vi = Ricerca ( Nuovo_Stato ) } se E ≠ Risultato = max (Valori) altrimenti Risultato = -∞ (Fallimento)

restituisci Risultato}

Page 16: Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010.

Giochi con AvversarioI Giochi a 2 giocatori “a conoscenza completa ” devono avere le seguenti caratteristiche:

• Il gioco è definito da una serie di regole che definiscono le mosse lecite.

• Le mosse sono fatte alternativamente da 2 giocatori A e B.

• Ogni giocatore ha informazioni complete sullo stato del gioco in ogni istante.

• Non c’è intervento del caso (sistema deterministico).

• Uno Stato Terminale rappresenta una situazione di Vittoria, Sconfitta o Pareggio per un giocatore.

Diremo che un gioco è Equo, quando nessuno dei due giocatori può vincere se l’avversario gioca “al meglio ”.

Page 17: Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010.

Procedura Neg-MaxUsiamo il backtracking ricorsivo per trovare la mossa migliore;ad ogni passo verrà scelta la mossa che è più conveniente per A o per B.

La funzione di valutazione capovolge il parere dell’avversario, negando il risultato restituito dalla successiva istanza ricorsiva.

Stabilito un valore positivo per la vittoria, uno negativo per la sconfitta e Zero per il pareggio:

• A esamina lo stato attuale del gioco e cataloga le possibili mosse.

• A esegue ad una ad una le mosse possibili.• Se la mossa è terminale, assegna

il valore (es. +1, -1, 0).• Se la mossa non è terminale, consegna lo stato

del gioco a B chiedendo il suo parere (ricorsione).

• B risponderà secondo il suo punto di vista: ad esempio restituirà +1 se è certo di vincere.

• A trasforma il parere di B, assumendo -1 per quella mossa.

• A conclude l’analisi indicando come miglior mossa quella che ha il valore più alto dal suo punto di vista.

Page 18: Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010.

Tic-Tac-ToeCiascuno dei due giocatori inserisce a turno una pedina del proprio colore sul tavolo di gioco, fino a formare un Tris, ovvero una fila di tre pedine dello stesso colore, in orizzontale, in verticale o in diagonale: vince chi realizza per primo un tris; riempito il campo senza tris, la partita è Pari.

Struttura Dati:

Tavola[10] scacchiera del gioco; sono usate le caselle da [1] a [9]

ogni casella vale 0 se vuota, 1 o 2 se occupata.

Nvuote numero delle caselle ancora vuote a disposizione

Who giocatore che deve muovere: 1 o 2

DoveGioco casella della prossima mossa da eseguire

Finito 0 = gioco in corso1 = gioco finito (vinto 1)2 = gioco finito (vinto 2)3 = gioco finito in pareggio27 = gioco finito per abbandono

Page 19: Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010.

/*==================================================================== Esempio base per il gioco del Tris tra due giocatori scelti tra: -) Umano ( tramite keyboard ) -) PC ( tramite sub di valutazione mossa ). Manca un’interfaccia utente e la possibilità di scegliere chi inizia.---------------------------------------------------------------------*/main (void){ int Tavola[10], // scacchiera di gioco Nvuote, // numero delle caselle vuote (con valore 0) Who, // giocatore che deve muovere: 1 o 2 DoveGioco, // mossa da fare da parte di Who i, // indice generico Finito; // 0 = gioco in corso // 1,2 = gioco finito (vinto 1 o 2) // 3 = gioco finito in pareggio // 27= gioco finito per abbandono for (Nvuote = i= 9; i; Tavola[i--]=0 ); // pulizia scacchiera StampaTavola (Tavola); // visualizza la tavola

Who = 1; // muove per primo il PC assegnato a 1 Finito = 0; while (! Finito) { if (Who == 1) // mossa decisa dalla funzione di valutazione { ValutaMossa (Tavola, Nvuote, Who, &DoveGioco); } else // Who == 2: mossa del giocatore umano tramite keyboard

Page 20: Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010.

else // Who == 2: mossa del giocatore umano tramite keyboard { DoveGioco = 0; do { i = getch(); if (i == 27) // se si vuole abbandonare il gioco Finito = 27; else if (i>=49 && i<=57 && Tavola[i-48]==0) //solo case vuote DoveGioco = i-48; } while (!DoveGioco && !Finito); }

if (!Finito) // se non c'è stato un abbandono { Tavola[DoveGioco] = Who; // occupa la casella if ( VerificaTris(Tavola) ) Finito = Who; // mossa vincente else if (Nvuote == 1) // se unica vuota, pareggio Finito = 3; else // passa all'avversario { Who = 3 - Who; Nvuote --; } } StampaTavola (Tavola); // Visualizza il campo di gioco } while (!Finito);

printf ("Partita terminata"); getch();}

Page 21: Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010.

/*========================================================== Verifica se esiste un tris qualsiasi sulla tavola di gioco Considerando i valori 1 e 2 in binario, 10 e 01, l’AND di tre caselle è diverso da zero solo se sono uguali.

Output : 1 l'ultima mossa effettuata ha prodotto un Tris 0 non esistono tris sulla tavola ---------------------------------------------------------*/int VerificaTris( int *Tav // scacchiera di gioco){ int Fatto;

Fatto = ( Tav[1] & Tav[2] & Tav[3] || // 1ø rigo Tav[4] & Tav[5] & Tav[6] || // 2ø rigo Tav[7] & Tav[8] & Tav[9] || // 3ø rigo Tav[1] & Tav[4] & Tav[7] || // 1ø colonna Tav[2] & Tav[5] & Tav[8] || // 2ø colonna Tav[3] & Tav[6] & Tav[9] || // 3ø colonna Tav[1] & Tav[5] & Tav[9] || // diagonale / Tav[7] & Tav[5] & Tav[3] ); // diagonale \

return Fatto; }

Page 22: Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010.

/* Valutazione della mossa migliore in un dato stato del gioco. si ferma nell'analisi quando trova una mossa vincente. Output: valore della scacchiera vista da Who che deve muovere -1 sconfitta, 0 pareggio, +1 vittoria. */int ValutaMossa( int *Tav, // scacchiera di gioco int Nvuote, // numero delle caselle vuote (valore 0) int Who, // giocatore che deve muovere: 1 o 2 int *DoveMeglio // casella della mossa migliore){ int i, // indice sulle caselle della scacchiera Dove, // miglior contromossa dell'avversario Vale, // valore della contromossa MaxVale; // valore della miglior mossa possibile for (MaxVale = -INFINITO, i=9; i && MaxVale<1; i--) if (Tav[i]==0) // se la casella è vuota { Tav[i] = Who; // occupa la casella if ( VerificaTris(Tav) ) // se si è creato un tris Vale = +1; // mossa vincente else if (Nvuote == 1) // se unica vuota, pareggio Vale = 0; else // chiede il parere dell'avversario Vale = -ValutaMossa (Tav, Nvuote-1, 3-Who, &Dove); if (Vale > MaxVale) { MaxVale = Vale; *DoveMeglio = i;} Tav[i] = 0; // libera la casella } return MaxVale;}

Page 23: Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010.

Hexagon 19

Sia assegnata una scacchiera con 19 caselle esagonali (inizialmente vuote).

Due giocatori (Nero e Bianco) collocano alternativamente una pedina in una casella vuota; il primo che è costretto ad occupare due caselle adiacenti, ha perso la partita.1. Costruire una procedura che valuti la mossa migliore da compiere in qualsiasi situazione di gioco (considerando che anche l’avversario giocherà sempre “al meglio”), utilizzando il Backtracking ricorsivo.

2. Utilizzare la suddetta procedura per stabilire se si può sicuramente vincere giocando per primi o per secondi (un pareggio è impossibile).

3. Consentire ad un umano di giocare contro il computer, sia pure con un’interfaccia spartana.

Page 24: Risoluzione di Problemi con gli algoritmi Ricorsivi Laboratorio di Algoritmi e Strutture Dati Proff. Francesco Cutugno e Luigi Lamberti 2009-2010.

Bibliografia Web

http://it.wikipedia.org/wiki/Ricorsione

http://www.cad.polito.it/~bernardi/ corsi/APA_IVREA/APA2/W4/minmax.pdf

http://www.di.unipi.it/~simi/ AI/SI2007/Lucidi/games.pdf

http://www.cs.unibo.it/~cianca/ wwwpages/chesssite/tozzi.pdf