Codice PHP:
#Logica di base:
#1) Cercare l'insieme di soluzioni ammissibili per ogni punto della matrice (vedi setSolution)
#2) Vedere (Main loop) se queste soluzioni sono "uniche" cioe' se:
#2a) una cella ammette una sola soluzione
#2b) una soluzione e' unica nella riga, colonna o blocco
#Se 2a o 2b sono vere abbiamo risolto un nuovo punto e si torna ad 1
#Si ripete il tutto finche' non c'e' piu' nulla di nuovo da fare,
#nel qual caso abbiamo risolto oppure la matrice e irrisolvibile dati i punti iniziali
#in ogni caso abbiamo finito
#m e' la matrice iniziale
m=[
[5, 2, 6, 8, 0, 0, 3, 7, 0],
[3, 0, 0, 0, 9, 0, 5, 0, 0],
[0, 8, 0, 0, 5, 0, 0, 0, 0],
[0, 0, 0, 2, 0, 0, 0, 0, 0],
[0, 0, 7, 0, 0, 3, 2, 0, 0],
[8, 6, 0, 9, 0, 0, 0, 5, 0],
[0, 0, 0, 0, 0, 0, 0, 6, 1],
[0, 0, 0, 0, 7, 0, 0, 4, 0],
[1, 7, 8, 6, 0, 0, 0, 0, 0],
]
print "\n============================="
#Stampiamo le righe di m, si puo' anche usare "print m"
#ma il risultato sotto e' piu' carino
for x in m: print x
#Alcune variabili che contengono vettori di numeri usati di frequente
#rispettivamente: 1-9, 0-8, e 0-3
#set9 non e' in realta' un vettore ma un insieme.
#L'insieme e' un contenitore speciale che supporta
#operazioni di insiemistica (a noi servono unione e differenza di insiemi)
set9 = set(range(1,10))
range9 = range(9)
range3 = range(3)
#points e' una lista che contine i punti della matrice m da risolvere
#in pratica m contiene i punti risolti, points i punti da risolvere
#in points ogni punto e' una coppia di variabili (i,j)
#dove i e' la riga e j la colonna di ogni punto
#la lista viene inizializzata con i punti di m che sono = 0
#l'utilizzo di [f(x,y) for x in range1 for y in range2 if ... ]
#e' un modo piu' sintetico per creare una lista
#invece di usare loops tradizionali.
#Tale sintassi e' usata anche in seguito
points = [(i,j) for i in range9 for j in range9 if m[i][j]==0]
#solutions e' una matrice (stesse dimensioni di m)
#in cui ogni punto e' un insieme di soluzioni ammissibili.
#notare che per creare la matrice si e' usato
#[[f(x,y) for x in range] for y in range]
#simile a quanto fatto per points ma qui si ottiene una lista di liste
#la matrice viene iniziliazzata con insiemi vuoti
solutions = [[set() for i in range9] for j in range9]
#Questa funzione (f) trasforma una riga/colonna in m
#nella prima riga/colonna del sottoblocco corrispondente
#es 0->0, 1->0, 2->0, 3->1, 4->1...
#siccome x e' un intero x/3 e' un intero
def f(x): return (x/3)*3
#Questa funzione viene invocata per inizializzare solutions
#ed ogni qual volta si risolve un nuovo punto. Il suo compito e'
#1) aggiornare m (inserendo il nuovo punto) e points (eliminando il punto).
#2) ricalcolare l'insieme delle soluzioni ammissibili (solutions) di ogni punto
def setSolution(r, c, sol=None):
#doloop e' usato per controllare il loop principale
global doloop
doloop = True
#Se setSolution non e' chiamato in fase di inizializzazione (sol=None)
#vengono aggiornati m, points e solutions
if sol is not None:
print "row=", r+1, "col=", c+1, ":", sol
m[r][c] = sol
points.remove((r,c))
solutions[r][c] = set()
#Per ogni punto da risolvere vengono calcolate le soluzioni ammissibili
#Queste sono date dall'inisieme dei numeri 1-9 (set9)
#a cui va sottrato l'insieme dei punti gia' risolti
#sulla stessa riga, stessa colonna, e stesso blocco
for i,j in points:
row = set(m[i])
col = set(cl[j] for cl in m)
block = set(m[f(i)+i2][f(j)+j2] for i2 in range3 for j2 in range3)
solutions[i][j] = set9.difference(row.union(col).union(block))
#Non tutte le soluzioni sopra pero' sono possibili...
#Alcune sono mutuamente incompatibili. Possiamo scremare.
#Se ad es si prende l'insieme delle soluzioni ammissibili
#della prima riga di un blocco ed in
#queste appare il numero 2 come possibile soluzione,
#ed il numero 2 non appare nell soluzioni delle altre due righe del blocco,
#significa che 2 deve essere nella prima riga del blocco
#il che' significa che 2 non puo' essere nel resto della riga
for i,j in points:
blockrow = set([n for i2 in range3 for j2 in range3 for n in solutions[f(i)+i2][f(j)+j2] if f(i)+i2==i]).difference(set([n for i2 in range3 for j2 in range3 for n in solutions[f(i)+i2][f(j)+j2] if f(i)+i2!=i]))
blockcol = set([n for i2 in range3 for j2 in range3 for n in solutions[f(i)+i2][f(j)+j2] if f(j)+j2==j]).difference(set([n for i2 in range3 for j2 in range3 for n in solutions[f(i)+i2][f(j)+j2] if f(j)+j2!=j]))
if len(blockrow):
for j2 in range9:
if j2 not in [f(j),f(j)+1,f(j)+2]:
solutions[i][j2]=solutions[i][j2].difference(blockrow)
if len(blockcol):
for i2 in range9:
if i2 not in [f(i),f(i)+1,f(i)+2]:
solutions[i2][j]=solutions[i2][j].difference(blockcol)
#Chiamando setSolutions senza terzo argomento
#in pratica inizializziamo la matrice solutions
setSolution(0, 0)
#Loop principale. Controlla per ogni punto da risolvere (i,j in points)
#e per ogni soluzione ammissibile (n) di tale punto,
#se tale soluzione e' "unica" cioe' se:
# 2a) una cella ammette una sola soluzione: len(solutions[i][j])==1
# 2b) una soluzione e' unica nella riga, colonna o blocco.
while(doloop):
#Il loop si interrompe al prossimo giro
#una volta controllati tutti i punti a meno che non risolviamo
#qualche nuovo punto. In tal caso doloop viene riattivato da
#setSolutions e facciamo un giro in piu'
doloop = False
#per ogni punto
for i,j in points:
row = set([n for k in range9 for n in solutions[i][k] if j != k])
col = set([n for k in range9 for n in solutions[k][j] if i != k])
block = set([n for i2 in range3 for j2 in range3 for n in solutions[f(i)+i2][f(j)+j2] if f(i)+i2 !=i or f(j)+j2 != j])
#per ogni soluzione ammissibile associata a quel punto
for n in solutions[i][j]:
#controlliamo se n e' la soluzione giusta
if len(solutions[i][j])==1 or n not in row or n not in col or n not in block:
#bingo! abbiamo risolto un nuovo punto
setSolution(i,j,n)
#Finito! formattiamo l'output... Simile a quanto fatto sopra,
#siccome non e' detto che tutti i punti vengano risolti
#inseriamo nel risultato anche le soluzioni ammissibili
m = [[m[i][j]and m[i][j]or [x for x in solutions[i][j]] for j in range9]for i in range9]
for x in m: print x