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.