Fondamenti di informatica
Estendere un linguaggio – nuovi domini di dati (II)
 Obiettivi di apprendimento
 generali
 uso del meccanismo list per costruire rappresentazione di oggetti strutturati
– in particolare, matrici bi-dimensionali
 strategia di soluzione basata su ripartizione di un problema in sottoproblemi
(divide et impera)
– ognuno risolto da una specifica funzione
 riuso di soluzioni (algoritmi)
 caso di studio
 sudoku
2
Caso di studio: giocare a sudoku
 griglia 9x9 da riempire con cifre da 1 a 9
  senza ripetizioni in righe, colonne,
senza ripetizioni in nessuno dei 9 blocchi 3x3
 un tipico enigma sudoku
 … e la sua soluzione
3
Giocare a sudoku
 In effetti, il nostro obiettivo non è costruire un risolutore di enigmi
sudoku
 … ma un verificatore automatico della correttezza di una soluzione
proposta
 abbiamo bisogno di :
 una rappresentazione della griglia di gioco
 un algoritmo per il controllo della correttezza
4
Una griglia è una matrice
 Disposizione bi-dimensionale di elementi
  matrici numeriche utilizzate in algebra lineare
…
 ogni elemento individuato dalle sue coordinate (riga,colonna)
 esempio di matrice
 matrice di valori interi con 4 righe e 5 colonne
 l’elemento (0,0) è 3
colonne
 l’elemento (0,2) è 1
0
1
2
3
4"
0"
3"
5"
1"
0"
9"
1"
12"
16"
12"
34"
2"
2"
0"
0"
23"
5"
11"
7"
51"
8"
0"
33"
 l’elemento (2,3) è 5
 …
 nota : le matrici di pixel usate
per rappresentare immagini
avevano le loro
righe
coordinate invertite !!!
3"
5
Rappresentazione di matrici
 idea
 rappresentare una matrice come un aggregato di righe
1a riga
2a riga
n-ma riga
 … e ogni riga come un aggregato di valori (tutti dello stesso tipo)
1°
valore
2°
valore
m-esimo
valore
6
Uso di liste per rappresentare matrici bi-dimensionali
 Il meccanismo list può essere usato per costruire rappresentazioni di
aggregati
  … e liste di liste (sotto-liste annidate dentro altre liste) possono essere
usate per rappresentare aggregati di aggregati
… e così via
 costruzione di una matrice (con tutti gli elementi inizializzati a 0)
def matrix(numrows, numcolumns) :"
# @param numrows: int; number of rows"
# @param numcolumns: int; number of columns"
# @return: matrix (with the specified number of rows and columns"
#
all matrix entries are initialized to 0)"
mat = []"
for i in range(0, numrows) :"
mat.append([])"
>>> my_matrix=matrix(2,3)"
for j in range(0, numcolumns) :"
>>> print my_matrix"
mat[i].append(0)"
[[0, 0, 0], [0, 0, 0]]"
return mat "
7
Esempi
 stampare le righe su linee separate
def print_mat(matrix) :"
for row in matrix :"
print row"
>>> my_matrix=matrix(2,3)"
>>> print_mat(my_matrix)"
[0, 0, 0]"
[0, 0, 0]"
 assegnare un valore a un elemento di una matrice
 usando la notazione m[i][j]
>>> my_matrix[0][1]=3"
>>> print_mat(my_matrix)"
[0, 3, 0]"
[0, 0, 0]"
>>> my_matrix[1][2]=1+4"
>>> print_mat(my_matrix)"
[0, 3, 0]"
[0, 0, 5]"
8
Esempi
 costruzione di una matrice e lettura interattiva del valore dei suoi elementi
def read_matrix(numrows, numcolumns) :"
# @param numrows: int; number of rows"
# @param numcolumns: int; number of columns"
# @return: matrix; the matrix read from input"
#"
# builds a matrix with the specified number of rows and columns"
# and initializes its entries with values taken from input"
# one row at a time, specified as a list of numcolumns values"
m = matrix(numrows, numcolumns)"
for i in range(0, numrows) :"
m[i] = input("write a row as a list of values (enclosed in square brackets): ")"
return m"
9
Esempi
 algebra matriciale: somma di due matrici
def add(m1, m2) :"
# @param m1: matrix;"
# @param m2: matrix; "
# @return: matrix; (m1+m2)"
# m1 and m2 must have same number of rows and same number of columns"
# NOTE: len(mx) is the number of rows of mx"
# NOTE: len(mx[0]) is the number of columns of mx"
if len(m1)==len(m2) and len(m1[0])==len(m2[0]) :"
s = matrix(len(m1),len(m1[0]))"
for i in range(0,len(m1)) :"
for j in range(0,len(m1[0])):"
s[i][j] = m1[i][j]+m2[i][j]"
else :"
s = [] "
print "matrix dimensions do not match""
return s "
10
Torniamo al sudoku
 Problema: controllo della correttezza di una soluzione
 Input: soluzione proposta
 Strategia di soluzione: decomposizione in sottoproblemi
 quali?
 Algoritmo sudoku_check
     controlla le righe
controlla le colonne
controlla i blocchi
se un qualunque controllo fallisce, la soluzione proposta non è corretta
altrimenti è corretta
11
sudoku check: codice Python
def sudoku_check(sudoku_mat) :"
# @param sudoku_mat: matrix; (usually 9x9) it contains the proposed sudoku solution"
# @return: none"
# the called xxx_check() functions return a negative value when the check is OK"
# otherwise a non negative integer indicating the error location"
##### row check"
result = row_check(sudoku_mat)"
controllo righe
if result >= 0 :"
print "error at row " + str(result) #str(n) converts n to a string"
else : ##### column check"
result = col_check(sudoku_mat)"
controllo colonne
if result >= 0 :"
print "error at column " + str(result) #str(n) converts n to a string"
else : ##### block check"
result = block_check(sudoku_mat)"
controllo blocchi
if result >= 0 :"
print "error at block " + str(result) #str(n) converts n to a string"
#### final response"
if result >= 0 :"
print "your solution is not correct; try again""
else :"
print "your solution is correct; you are a good sudoku player" "
12
Controllo righe: algoritmo
 proposizione logica da verificare:
∀ riga della matrice : corretta(riga) == vero
 (pseudo) codice Python :
 for riga in matrice :
if not corretta(riga) :
return matrice non OK
return matrice OK
?
13
Controllo righe: codice Python (1)
 controllo su tutte le righe (quantificatore universale)
def row_check(sudoku_mat) :"
# @param sudoku_mat: matrix; a square matrix (usually 9x9) that contains the "
#
proposed sudoku solution "
# @return: int; a negative value if sudoku_mat is correct by row, "
#
otherwise a value>=0 indicating the first found incorrect row"
row_num = len(sudoku_mat)"
for i in range(0, row_num) :"
corretta(riga)
if not vect_check(sudoku_mat[i]) : # row i is not correct"
return i # stop execution and return row index"
return -11 # it could be any negative value"
14
Controllo singola riga
 Algoritmo
[7,2,8,1,8,9,1,3,4]scorretta
[7,2,8,1,5,9,6,3,4]corretta
errore! in posizione 7 (8-1) è già presente il
valore TRUE (indica che il valore 8 è stato già
incontrato in precedenza)
15
Controllo singola riga: codice Python
def vect_check(vect) :"
# @param vect: list; representing a uni-dimensional vector"
# @return: bool; TRUE if vect contains all integer values from 1 to len(vect), in any order; "
#
FALSE otherwise"
bool_vect = []"
# build a vector with same number of entries as vect, all initialized to false, "
for i in range(0,len(vect)) :"
bool_vect = bool_vect + [false]"
ok = true"
# start setting bool_vect entries to true, according to values contained in vect"
# if an entry of bool_vect results already true, then a value in vect is repeated at least twice (error)"
i = 0"
ok = true"
while ok and i<len(vect) :"
if bool_vect[vect[i]-1] == false :"
bool_vect[vect[i]-1] = true"
else :"
ok = false"
i = i+1"
16
return ok"
Controllo colonne: algoritmo
 Concettualmente identico al controllo righe
 … ma gli elementi di una colonna non fanno parte della stessa lista!
 nella rappresentazione di matrici che abbiamo adottato
[ [7, 2, 4, 8, 1, 9, 1, 3, 8],
[5, 5, 8, 8, 6, 4, 3, 6, 3],
[5, 1, 9, 5, 6, 7, 7, 7, 7],
[1, 1, 2, 7, 5, 8, 3, 4, 4],
[6, 7, 4, 1, 4, 8, 9, 6, 7],
[4, 5, 8, 8, 4, 7, 7, 9, 9],
[5, 2, 4, 9, 3, 8, 4, 3, 5],
[5, 7, 2, 4, 7, 8, 7, 8, 2],
[1, 1, 1, 8, 3, 2, 9, 8, 5] ]
 Possiamo
 … scrivere una soluzione ad hoc
 oppure
 … riusare (in modo appropriato) la
soluzione già usata per il controllo
righe
17
def matrix_col_to_vect(mat, j) :"
# @param mat: matrix; a bi-dimensional matrix"
# @param j: int; a column index for mat "
# @return: list; a uni-dimensional vector (represented as a list) built with all entries of the j-th column"
v = []"
for i in range(0, len(mat)): # len(mat) is the number of rows of mat"
v = v + [mat[i][j]]"
return v"
def col_check(sudoku_mat) :"
# @param sudoku_mat: matrix; a square matrix (usually 9x9) that contains the "
#
proposed sudoku solution "
# @return: int; a negative value if sudoku_mat is correct by column, "
#
otherwise an index j>=0 indicating the first found incorrect column"
col_num = len(sudoku_mat[0])"
for j in range(0,col_num) :"
col = matrix_col_to_vect(sudoku_mat,j)"
if not vect_check(col) : # column j is not correct"
return j # stop execution and return column index"
return -11 # it could be any negative value"
18
Controllo blocchi: algoritmo
 Concettualmente identico ai controlli precedenti (righe / colonne)
 … ma gli elementi di un blocco sono disposti su due dimensioni !
[ [7, 2, 4, 8, 1, 9, 1, 3, 8],
[5, 5, 8, 8, 6, 4, 3, 6, 3],
[5, 1, 9, 5, 6, 7, 7, 7, 7],
[1, 1, 2, 7, 5, 8, 3, 4, 4],
[6, 7, 4, 1, 4, 8, 9, 6, 7],
[4, 5, 8, 8, 4, 7, 7, 9, 9],
[5, 2, 4, 9, 3, 8, 4, 3, 5],
[5, 7, 2, 4, 7, 8, 7, 8, 2],
[1, 1, 1, 8, 3, 2, 9, 8, 5] ]
 Possiamo
 … scrivere una soluzione ad hoc
 oppure
 … riusare (in modo appropriato) la
soluzione già usata per righe / colonne
19
Controllo blocchi: codice Python (1)
 1: costruzione di una lista da un blocco (3x3) della matrice
def matrix_block_to_vec (mat, i, j) :"
# @param mat: matrix; a bi-dimensional (9x9) matrix"
# @param j: int; "
# @param j: int; i,j are the coordinates of the upper-left entry of the 3x3 block"
# @return: list; a uni-dimensional vector (represented as a list) built with all entries of the block"
b = []"
for h in range(i,i+3):"
for k in range(j,j+3):"
b = b + [mat[h][k]]"
return b "
20
Controllo blocchi: codice Python (2)
 2: … e poi controllo di tutti i blocchi (analogo a quello per righe / colonne)
def block_check(sudoku_mat) :"
# @param sudoku_mat: a square matrix (usually 9x9) that contains the proposed sudoku solution "
# @return: a negative value if sudoku_mat is correct by block, "
#
otherwise an index i>=0 indicating the first found incorrect block"
# blocks are numbered (starting from 1) from left to right, from top to bottom"
row_num = len(sudoku_mat)"
col_num = len(sudoku_mat[0])"
block_num = 1"
for i in range(0,row_num,3) : # i takes value in [0, 3, 6]"
for j in range(0, col_num,3) : # j takes value in [0, 3, 6] "
block = matrix_block_to_vec(sudoku_mat,i,j)"
if not vect_check(block) : # block block_num is not correct"
return block_num # stop execution and return block number"
block_num = block_num+1"
return -11 # it could be any negative value"
21
infine …
 Costruite una soluzione
def random_sudoku():"
# @return: a 9x9 matrix randomly filled with values from 1 and 9"
# Note: it is quite unlikely that you will get a correct sudoku solution :-)"
from random import *"
mat = matrix(9,9)"
for i in range(0,9):"
for j in range(0,9):"
mat[i][j] = 1 + int(9*random())"
return mat"
 … e poi controllatela !
>>> m = random_sudoku()"
>>> sudoku_check(m)"
error at row 0"
your solution is not correct; try again"
22
Che cosa abbiamo appreso
 Obiettivi di apprendimento
 generali
 uso del meccanismo list per costruire rappresentazione di oggetti strutturati
– in particolare, matrici bi-dimensionali
 strategia di soluzione basata su ripartizione di un problema in sottoproblemi
(divide et impera)
– ognuno risolto da una specifica funzione
 riuso di soluzioni (algoritmi)
 caso di studio
 sudoku
23