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