Lezione 2 - Fondamenti di informatica
Transcript
Lezione 2 - Fondamenti di informatica
Fondamenti di informatica II Un secondo problema Il problema • È data una griglia con n2 celle di lato (e quindi n2*2 celle in totale) • Ogni cella può contenere uno di n2 simboli. Per semplicità usiamo le cifre 1…n2 se n<4 • Il problema è di trovare una assegnazione dei simboli alle celle tale da non avere simboli ripetuti nelle righe, nelle colonne e in ognuna delle n2 sottogriglie n n Il caso n=3 (sudoku) Sudoku • In effetti il gioco classico consiste nel trovare la (sola) combinazione che rispetta alcuni vincoli iniziali: 5 8 3 3 9 4 8 9 6 9 6 4 7 1 7 1 6 9 2 1 5 6 5 4 8 2 7 9 Sudoku • Si presume che esista una sola soluzione. Ma un algoritmo corretto dovrebbe trovare una soluzione anche se non è unica. • I vincoli riducono il numero di possibili soluzioni. Ma se sono ridondanti ne facilitano la ricerca (per questo il problema può essere di diversi livelli di difficoltà) Il problema COME SI RISOLVE IL SUDOKU ? Caso particolare • Possiamo limitarci per semplicità al caso n=3, anche perché la generalizzazione è abbastanza ovvia – E molti hanno giocato con 81 celle … • Quindi, prima cosa da chiedersi: – Come si fa a risolvere a mano ? – E l’algoritmo (se c’è) può esserci utile ? L’algoritmo • L’approccio normalmente utilizzato nella soluzione manuale è di tipo analitico: – Sfruttando i punti fissi e i vincoli si cercano celle dove esiste una sola scelta – Oppure si procede con analisi di tipo “what-if” dove ipotizzando di fare una scelta si cerca di arrivare ad una contraddizione (che quindi esclude la scelta) Per una soluzione automatizzata • Nella ricerca di un algoritmo da codificare devo considerare che un procedimento analitico è spesso complesso da descrivere in modo rigoroso – E potenzialmente lento nell’esecuzione • In compenso un algoritmo più semplice (che “ragiona” meno) sarà più facile da scrivere e più veloce nell’valutare alternative ma potrebbe dover esaminare più casi… Serve un punto di partenza QUINDI ? Il Sudoku automatico • Una ipotesi di algoritmo potrebbe sinteticamente essere: – se non ci sono celle vuote il problema è risolto – Altrimenti trova una cella dove si può mettere un numero rispettando tutti i vincoli, e prova a risolvere questo nuovo problema (con questo stesso algoritmo) – Se ci riesci hai risolto – Altrimenti prova con un’altra scelta – Se non c’è un’altra scelta allora il problema non ha soluzione Più formalmente • La funzione Sudoku riceve in input un vettore V 9 x 9. – Ogni elemento del vettore contiene un numero tra 1 e 9 se la cella è occupata oppure 0 se la cella è libera • La funzione restituisce un valore booleano: – Se vero il vettore contiene la soluzione – Se falso il vettore è inalterato e la soluzione non esiste La funzione Sudoku Per riga che va da 1 a 9 Per colonna che va da 1 a 9 Se V(riga, colonna) è zero Identificare la lista dei valori ammissibili per V(riga, colonna) Se non ve ne sono restituire falso Per ogni valore ammissibile i Porre V(riga, colonna) = i Chiamare Sudoku(V) Se restituisce vero allora restituire vero Porre V(riga, colonna) = 0 Restituire falso Restituire vero Identificare la lista … ? • La prima cosa che serve è un modo per ricordare quali numeri sono ammissibili; Bool C[10]; • Poi vado per riga e per colonna a cercare quelli che sono già presenti: for (i=1; i<10; i++) if (v[row][i] != 0) c[v[row][i]] = true; for (i=1; i<10; i++) if (v[i][clm] != 0) c[v[i][clm]] = true; • E per la sottogriglia ? E per la sottogriglia ? • È un loop banale, come gli altri, ma devo trovare l’angolo superiore sinistro da cui partire • In effetti se la cella in questione è in x,y allora la sottogriglia parte in x1, y1 che sono i massimi multipli di n minori di x,y rispettivamente • E come trovo il massimo multiplo ad esempio di 3 minore di x ? Il massimo multiplo • In possibile approccio (che ha applicazione in altri problemi analoghi) è di sfruttare l’aritmetica intera della cpu: • x1 = (x /n) * n ( oppure x1 = (x/3)*3 ) • se x1, x, n sono interi e il compilatore genera codice che usa interi anche per i risultati intermedi allora il risultato sarà quello voluto • Ma esiste una soluzione più efficiente … Quindi per la sottogriglia r1=(r/3)*3; c1=(c/3)*3; for (i=r1; i<r1+3; i++) for (j=c1; j<c1+3; j++) if (v[i][j] != 0) c[v[i][j]] = true; Ma esiste una soluzione più efficiente … Infine • A questo punto abbiamo in c (vettore di n elementi) la lista dei valori ammissibili per la cella che stiamo esaminando. • Ci basta provarli uno alla volta: for (i=0; i<9; i++) if (c[i] == false) { v[row][clm]=i; if (sudoku(v)) return (true); } v[row][clm]=0; return(false); Note finali • La funzione Sudoku così costruita è in grado di trovare una soluzione (se esiste) per qualsiasi problema, o di stabilire che la soluzione non esiste. • È possibile aggiungere la logica necessaria a stabilire se esistono più soluzioni e ad esempio a contarle • La funzione non è ottimale: – Come si potrebbe renderla più efficiente ? Ma si può far meglio ? OTTIMIZZAZIONE Come ragiona questa funzione ? • Possiamo descrivere il processo decisionale e l’algoritmo come l’attraversare un albero, dove alla radice è la situazione iniziale ed i rami sono le possibili alternative che di volta in volta esaminiamo. • L’algoritmo scende lungo i rami fono a quando si ferma – Perché ha trovato la soluzione – Perché ha trovato che non si può proseguire (una contraddizione) L’albero 1 2 3 5 4 6 8 7 9 Come migliorare ? • Innanzi tutto certe mosse sono commutabili. Ovvero rami diversi portano alla stessa soluzione, ma con una diversa sequenza di mosse. • Quindi conviene seguire (se possibile) le vie più brevi, e esaminare prima i rami che hanno maggiore probabilità di arrivare al risultato. • Ad esempio, perché non esaminare prima i nodi che hanno un solo discendente. Lì non ho alternative, non ci sono decisioni da prendere.