Introduzione Informatica II Capitolo 16 Backtrack Abbiamo visto che esistono diverse classi di problemi (decisionali, di ricerca, di ottimizzazione) Sono tutte definizioni basate sul concetto di soluzione ammissibile: una soluzione che soddisfa un certo insiemi di criteri/vincoli Problemi tipici che possono rivestire interesse, sono Contare le soluzioni ammissibili Costruire/trovare almeno una o tutte le soluzioni ammissibili Trovare le soluzioni ammissibili più grandi, più piccole o in generale ottimali Esempio: Elencare tutti i sottoinsiemi di k elementi di un insieme S Adattamento delle slide originali di A.Montresor. Disponibili secondo Creative Commons Attribution-NonCommercial-ShareAlike License. 2 Alcuni commenti Alcuni commenti Enumerazione Consiste nell elencare algoritmicamente tutte le possibili soluzioni ammissibili di un dato problema. Ovvero si esplora lo spazio di ricerca (spazio delle soluzioni) Costruire almeno una soluzione Si utilizza l'algoritmo per elencare tutte le soluzioni, fermandosi alla prima Contare le soluzioni In molti casi, è possibile contare in modo analitico Esempio: Se S = n, allora il # sottoinsiemi di k elementi: In altri casi, si costruiscono le soluzioni e si contano man mano che si trovano Trovare le soluzioni ottimali Si costruiscono/trovano tutte le soluzioni ad una ad una. Per ogni soluzione si valuta una funzione di costo Ovviamente in molti casi si possono utilizzare altre tecniche per trovare una soluzione ottimale. Per esempio programmazione dinamica, greedy, 3 4
Costruire tutte le soluzioni? Per costruire tutte le possibili soluzioni, si utilizza un approccio brute-force : Ovvero: si esamina interamente lo spazio delle possibili soluzioni Se sospettate che possa essere un approccio inefficiente, non sbagliate. Ma: spesso è l'unica strada possibile (o non ce ne sono di più efficienti) Tuttavia: a volte non è necessario analizzare l'intero spazio delle soluzioni Filosofia: Prova a fare qualcosa, e se non va bene, disfalo e prova qualcos'altro Ovvero: se nel costruire una soluzione devi compiere una scelta, falla e poi se scopri che era la scelta sbagliata torna indietro e scegli diversamente. Come funziona? Un metodo sistematico per iterare su tutte le possibili istanze di uno spazio di ricerca E' una tecnica algoritmica generale che, come altre, deve essere specializzata per trattare il particolare problema che si sta affrontando 5 6 Descrizione generale della tecnica: Una soluzione viene rappresentata come un vettore S[1..n] Il contenuto degli elementi S[i] è preso da un insieme di possibili scelte C, dipendente dal problema Esempi: C insieme generico, possibili soluzioni permutazioni di C: per ogni i scelgo un elemento di C (e lo tolgo da C in preparazione delle scelte successive) C insieme generico, possibili soluzioni sottoinsiemi di C: per ogni i scelgo se prendere o meno l elemento i-esimo di C C mosse di gioco, possibili soluzioni sequenze di mosse: per ogni i scelgo una delle mosse permesse (nota che le mosse permesse potrebbero essere diverse a seconda di i) C archi di un grafo, possibili soluzioni percorsi sul grafo: per ogni i scelgo un arco uscente dal nodo in cui sono arrivato (di volta in volta posso scegliere solo tra un sottoinsieme degli archi) 7 Come procedere: Ad ogni passo, partiamo da una soluzione parziale S[1..k] (all inizio k=0 e la soluzione parziale è vuota ) Se S[1..k] è una soluzione ammissibile, la processiamo in qualche modo (e se ne volevamo una sola, abbiamo finito, altrimenti si continua) Altrimenti (se non è ammissibile): Se è possibile, estendiamo S[1..k] compiendo una delle possibili scelte ed ottenendo una soluzione S[1..k+1]. Valutiamo ora la nuova soluzione procedendo ricorsivamente Altrimenti (se non è possibile estendere), cancelliamo l ultima scelta fatta (ritornando indietro nella ricorsione) ovvero rimuoviamo l'elemento S[k] (backtrack) e ripartiamo dalla soluzione S[1..k-1] (compiendo una scelta diversa da quella/e cancellata/e) Oss: Si può fare backtrack su più passi di ricorsione 8
Spazio di ricerca albero di decisione Soluzioni foglie in un albero di decisione Soluzioni parziali nodi interni dell'albero di decisione Radice soluzione parziale vuota Lo spazio di ricerca può essere ridotto (o possiamo visitarne solo una parte): Ad esempio i rami dell'albero che sicuramente non portano a soluzioni ammissibili possono essere potati (pruned) Per scoprire se posso potare un sottoalbero, devo compiere una valutazione in corrispondenza della soluzione parziale, cioè della radice del sottoalbero da potare Esempio: un circuito in un grafo che pesi meno di k, 9 10 : algoritmo generico Due possibili approcci per implementare la ricerca/visita: Ricorsivo: procede compiendo una visita in profondità nell'albero delle scelte, basata su un approccio ricorsivo Iterativo: utilizza un approccio greedy, eventualmente tornando sui propri passi 11 12
Esempio 1 Esempio 1: commenti Problema Elencare tutti i sottoinsiemi di un insieme S Domanda: Quali sono le scelte possibili? Commenti Non c'è pruning: tutto lo spazio possibile viene esplorato. In che ordine vengono stampati gli insiemi? E possibile pensare ad una soluzione iterativa? (che non usa backtracking) 13 14 Esempio 1: versione iterativa Esempio 2 Problema Elencare tutti i sottoinsiemi di dimensione k di un insieme S Versione iterativa Qual è il costo? Soluzione basata su backtracking Possiamo potare? 15 16
Esempio 2: un tentativo Esempio 2: tentativo migliore Si potrebbe potare qualcosa? 17 18 Esempio 3 n Regine Problema Stampa di tutte le permutazioni di un insieme A Rispetto al problema precedente: L'insieme dei candidati dipende dalla soluzione parziale corrente Problema: posizionare n regine in una scacchiera n n, in modo tale che nessuna regina ne minacci un'altra. Commenti storici: Il problema classico, con n=8 Metodo Partiamo dall'approccio più ingenuo, e via via raffiniamo la soluzione. 19 20
n Regine Idea: ci sono n 2 caselle dove piazzare una regina Algoritmo: S[1..n 2] array binario : S[i] = true regina in S[i] controllo soluzione: quando i = n 2 choices(s, n, i) : pruning: num. di soluzioni per n=8 2 64 ~ 1.84 10 19 Commenti: { true, false} {} se la nuova regina minaccia le regine esistenti Forse non abbiamo scelto la rappresentazione migliore La matrice binaria è molto sparsa n Regine Idea: Dobbiamo piazzare n regine, ci sono n 2 caselle Algoritmo S[1..n] coordinate in 1..n 2 : controllo soluzione: S[i] coordinata della regina i se i = n choices(s, n, i) : {1 n 2} pruning: ritorna il sottoinsieme di posizioni legali num. di soluzioni per n=8 (n 2) n = 64 8 = 2 48 ~ 2.81 10 14 Commenti C'è un miglioramento, ma lo spazio è ancora grande... Domanda: una soluzione 1-7-... come si distingue da 7-1-...? 21 22 n regine Idea: la regina i non può stare in una casella precedente alla regina i-1 (S[i-1] < S[i]) Algoritmo S[1..n] coordinate in 1..n 2 controllo soluzione: S[i] coordinata della regina i se i = n choices(s, n, i) {1 n 2} n regine Idea: ogni riga della scacchiera deve contenere esattamente una regina. Infatti non ne può contenere 2 o più, e se ne contenesse 0 un'altra riga dovrebbe contenerne 2 Algoritmo S[1..n] valori in 1..n controllo soluzione S[i] colonna della regina i se i = n choices(s, n, i) {1 n} pruning: ritorna posizioni legali, S[i-1] < S[i] pruning ritorna le colonne legali num. di soluzioni per n=8 (n 2) n / n! = 6 48 / 40320 ~ 6.98 10 9 Commenti: Abbiamo ridotto molto, ma si può ancora fare qualcosa num. di soluzioni per n=8 n n = 8 8 ~ 1.67 10 7 Commenti Quasi alla fine 23 24
n regine Idea: anche ogni colonna deve contenere esattamente una regina; i numeri 1..n devono apparire in S[1..n] come permutazione Algoritmo Modifichiamo l'algoritmo delle permutazioni per verificare anche le diagonali num. di soluzioni per n=8 n! = 40320 ~ 4.03 10 4 num. soluzioni effettivamente visitate: 15720 Giro di cavallo Problema: Si consideri ancora una scacchiera n n; lo scopo è trovare un giro di cavallo, ovvero un percorso di mosse valide del cavallo in modo che ogni casella venga visitata al più una volta Soluzione: Tramite backtrack 25 26 Giro di cavallo Giro di cavallo Soluzione Matrice n n le cui celle contengono 0 se la cella non è mai stata visitata i se la cella è stata visitata al passo i num. soluzioni: 64! ~ 10 89 Ma: ad ogni passo ho al massimo 8 successive caselle possibili, quindi ne visito al più 8 64 ~10 57 In realtà, grazie al pruning ne visito molto meno 27 28
Sudoku Sudoku Suuji wa dokushin ni kagiru Sudoku 29 Generazione labirinti 30 Esercizio Come generare un labirinto in una scacchiera n n? Come uscire da un labirinto? Esempio: 31 32
Un ultimo puzzle Esercizio Si consideri ancora una scacchiera n n, con n=2 k Qualsiasi scacchiera di questo tipo con una cella rimossa può essere ricoperta da triomini a forma di L Trovare un algoritmo che trovi una possibile ricopertura della scacchiera 33