Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
курсач.docx
Скачиваний:
51
Добавлен:
25.11.2018
Размер:
1.35 Mб
Скачать

Решатель судоку методом полного перебора

def solve(s):

try:

i = s.index(0)

except ValueError:

# No empty cell left: solution found

return s

c = [s[j] for j in range(81)

if not ((i-j)%9 * (i//9^j//9) * (i//27^j//27 | (i%9//3^j%9//3)))]

for v in range(1, 10):

if v not in c:

r = solve(s[:i]+[v]+s[i+1:])

if r is not None:

return r

class Sudoku(list):

'''Sudokus with nicer IO'''

def __init__(self, content):

list.__init__(self, [int(i) for i in content.split()]

if isinstance(content, str) else content)

def __str__(self):

return '\n'.join(

' '.join([(str(j) if j != 0 else '-')

for j in self[i*9:(i+1)*9]]) for i in range(9))

Решатель судоку аналитическими методами

TRIPLETS = [[0,1,2],[3,4,5],[6,7,8]]

#Row/Col/3x3 iteration list, each is nine lists of nine (row,col) pairs

ROW_ITER = [[(row,col) for col in range(0,9)] for row in range(0,9)]

COL_ITER = [[(row,col) for row in range(0,9)] for col in range(0,9)]

TxT_ITER = [[(row,col) for row in rows for col in cols] for rows in TRIPLETS for cols in TRIPLETS]

class sudoku:

def __init__(self, start_grid=None) :

#Setup list of lists (the rows), each row is a list of 9 cells, which are each a list of integers 1-9 inclusive.

self.squares =[ [range(1,10) for col in range(0,9)] for row in range(0,9)]

if start_grid is not None:

if len(start_grid)==81 :

for row in range(0,9) :

self.set_row(row, start_grid[(row*9):((row+1)*9)])

else :

assert len(start_grid)==9, "Bad input!"

for row in range(0,9) :

self.set_row(row, start_grid[row])

#self.check()

self._changed=False

def solved(self) :

for row in range(0,9) :

for col in range(0,9) :

if len(self.squares[row][col]) > 1 :

return False

return True

def copy(self) :

sudoku_copy = sudoku(None)

for row in range(0,9) :

for col in range(0,9) :

sudoku_copy.squares[row][col] = self.squares[row][col][:] #copy!

sudoku_copy._changed=False

return sudoku_copy

def set_row(self,row, x_list) :

assert len(x_list)==9

for col in range(0,9) :

try :

x = int(x_list[col])

except :

x = 0

#self.set_cell(row,col,x)

self.set_cell(row,col,x)

def set_cell(self,row,col,x):

if self.squares[row][col] == [x] :

#Already done!

pass

elif x not in range(1,9+1) :

#Set to unknown

pass

else:

assert x in self.squares[row][col], \

"Told to set square (%i,%i) to an impossible entry, %i" % (row,col,x)

self.squares[row][col] = [x]

self.update_neighbours(row,col,x)

self._changed=True

def cell_exclude(self, row,col,x) :

assert x in range(1,9+1)

if x in self.squares[row][col] :

#Remove it...

self.squares[row][col].remove(x)

#Should be one or more entries left...

assert len(self.squares[row][col]) > 0, \

"Removed last possible entry for square (%i,%i) which was %i" \

% (row, col, x)

#Now, has this confirmed the value for this square?

if len(self.squares[row][col]) == 1 :

#This cell is now definate..

#Need to update its friends...

#print "After exluding %i, square (%i,%i) must be %i" \

#% (x, self.row, self.col, self[0])

self._changed=True

self.update_neighbours(row,col,self.squares[row][col][0])

else :

#Don't need to remove this, already done!

pass

return

def row_exclude(self, row, x) :

assert x in range(1,9+1)

for col in range(0,9) :

self.cell_exclude(row,col,x)

def col_exclude(self, col, x) :

assert x in range(1,9+1)

for row in range(0,9) :

self.cell_exclude(row,col,x)

def update_neighbours(self,set_row,set_col,x) :

"""Call this when the square is set to x, either directly,

or as a side effect of an exclude leaving only one entry"""

#print "Updating (%i,%i) to be %i..." % (self.row, self.col, x)

#Update the possibilies in this row...

for row in range(0,9) :

if row <> set_row :

self.cell_exclude(row,set_col,x)

#Update the possibilies in this col...

for col in range(0,9) :

if col <> set_col :

self.cell_exclude(set_row,col,x)

#Update the possibilies in this 3x3 square...

for triplet in TRIPLETS :

if set_row in triplet : rows = triplet[:]

if set_col in triplet : cols = triplet[:]

#Only need to do four of the eight possibles (well, 9 if you count the cell itself)

#as did two on the row, and two on the col

rows.remove(set_row)

cols.remove(set_col)

for row in rows :

for col in cols :

assert row <> set_row or col <> set_col

#print "Updating (%i,%i) to be %i, excluding %i from (%i, %i)" \

#% (self.row, self.col, x, x, row, col)

self.cell_exclude(row,col,x)

def get_cell_int(self,row,col) :

if len(self.squares[row][col])==1 :

return int(self.squares[row][col][0])

else :

return 0

def get_cell_str(self,row,col) :

if len(self.squares[row][col])==1 :

return "(%i,%i) = %i" % (row, col, self.squares[row][col][0])

else :

return ("(%i,%i) = " % (row, col)) + ",".join([str(x) for x in self.squares[row][col]])

def get_cell_digit_str(self,row,col) :

if len(self.squares[row][col])==1 :

return str(self.squares[row][col][0])

else :

return "0"

def as_test_81(self) :

"""Return a string of 81 digits"""

return "".join(self.as_test_list())

def simple_text(self) :

return "\n".join(self.as_test_list())

def as_test_list(self) :

return [ ("".join( [self.get_cell_digit_str(row,col) for col in range(0,9)])) for row in range(0,9) ]

"""

answer=[]

for row in range(0,9) :

line=""

for col in range(0,9) :

line = line + self.get_cell_digit_str(row,col)

answer.append(line)

return answer

"""

def __repr__(self):

answer="[" + ",".join([ \

("[" + ",".join( [self.get_cell_digit_str(row,col) for col in range(0,9)]) + "]") \

for row in range(0,9) ])

return answer

def __str__(self):

answer = " 123 456 789\n"

for row in range(0,9) :

answer = answer + str(row+1) \

+ " [" + "".join([self.get_cell_digit_str(row,col).replace("0","?") for col in range(0,3)]) \

+ "] [" + "".join([self.get_cell_digit_str(row,col).replace("0","?") for col in range(3,6)]) \

+ "] [" + "".join([self.get_cell_digit_str(row,col).replace("0","?") for col in range(6,9)]) \

+ "]\n"

if row+1 in [3,6] :

answer = answer + " --- --- ---\n"

return answer

def retArr(self):

data = []

for row in range(0,9) :

data.append([])

for col in range(0, 9):

data[row].append(self.get_cell_digit_str(row,col))

return data

def check(self, level=0) :

self._changed=True

while self._changed:

#print "checking..."

self._changed=False

self.check_for_single_occurances()

self.check_for_last_in_row_col_3x3()

if level >= 1 :

self.overlapping_3x3_and_row_or_col() #(aka slicing and dicing)

if level >= 2 :

self.one_level_supposition()

if level >= 3 :

self.two_level_supposition()

#If nothing happened, then self.changed==False (still)

#and we break the loop

return

def check_for_single_occurances(self):

#Want to see if x only occurs once in this row/col/3x3...

for check_type in [ROW_ITER, COL_ITER, TxT_ITER]:

for check_list in check_type :

for x in range(1,9+1) : #1 to 9 inclusive

x_in_list = []

for (row,col) in check_list :

if x in self.squares[row][col] :

x_in_list.append((row,col))

if len(x_in_list)==1 :

(row,col) = x_in_list[0]

#This position MUST be be x

if len(self.squares[row][col]) > 1 :

self.set_cell(row,col,x)

def check_for_last_in_row_col_3x3(self):

#Now, for each row/col/3x3 want to see if there is a single

#unknown entry...

for (type_name, check_type) in [("Row",ROW_ITER),("Col",COL_ITER),("3x3",TxT_ITER)]:

for check_list in check_type :

unknown_entries = []

unassigned_values = range(1,9+1) #1-9 inclusive

known_values = []

for (row,col) in check_list :

if len(self.squares[row][col]) == 1 :

assert self.squares[row][col][0] not in known_values, \

"Already have %i (%i,%i) in known list [%s] for %s" % (self.squares[row][col][0],row,col, ",".join(map(str,known_values)), type_name)

known_values.append(self.squares[row][col][0])

assert self.squares[row][col][0] in unassigned_values, \

"Expected %i (%i,%i) in list [%s] for %s" % (self.squares[row][col][0],row,col, ",".join(map(str,unassigned_values)), type_name)

unassigned_values.remove(self.squares[row][col][0])

else :

unknown_entries.append((row,col))

assert len(unknown_entries) + len(known_values) == 9

assert len(unknown_entries) == len(unassigned_values)

if len(unknown_entries) == 1 :

#This cell must be the only number 1-9 not in known_values

x = unassigned_values[0]

(row,col) = unknown_entries[0]

#assert x not in known_values

#print "Because its the last cell in its row/col/3x3 entry (%i,%i) must be %i" \

#% (row,col,x)

self.set_cell(row,col,x)

"""

for row in range(0,9) : self.check_row(row)

for col in range(0,9) : self.check_col(col)

#Check the 3x3 squares...

for rows in TRIPLETS :

for cols in TRIPLETS :

for x in range(0,9) :

x_in_location=[]

for row in rows:

for col in cols :

if x in self.squares[row][col] :

x_in_location.append((row,col))

if len(x_in_location)==1 :

(row,col) = x_in_location[0]

#This position MUST be be x

if len(self.squares[row][col]) > 1 :

self.set_cell(row,col,x)

"""

return

def diagnosis(self) :

answer=""

df = long(1)

for row in range(0,9) :

for col in range(0,9):

if len(self.squares[row][col]) > 1 :

answer = answer + str(self.squares[row][col]) + "\n"

df = df * len(self.squares[row][col])

answer = answer + "Degrees of freedom: %i" % df

return answer

def overlapping_3x3_and_row_or_col(self):

"""Block and Column / Row Interactions (name from Simon Armstrong)

http://www.simes.clara.co.uk/programs/sudokutechnique3.htm

Also known as 'slicing and dicing'

"""

#For a given 3x3, and a given digit, want to see if

#all the remaining candidates are in a single row or column..

#Want to see if x only occurs once in this row/col/3x3...

for check_list in TxT_ITER :

for x in range(1,9+1) : #1 to 9 inclusive

#print "Checking %i in 3x3" % x, check_list

rows_for_x = []

cols_for_x = []

for (row,col) in check_list :

if x in self.squares[row][col] :

#print "Found possible %i at (%i,%i)" % (x, row, col)

if row not in rows_for_x : rows_for_x.append(row)

if col not in cols_for_x : cols_for_x.append(col)

#Are they all in the same row?

if len(rows_for_x)==1 and len(cols_for_x) > 1 :

#print "%i must be in row %i using cols %s" % (x, rows_for_x[0]+1, ",".join(map(lambda i : str(i+1),cols_for_x)))

#print self

#This means, we can remove X from all the rest of the row...

row = rows_for_x[0]

for col in range(0,9) :

if col not in cols_for_x :

self.cell_exclude(row,col,x)

#We can also remove x from all the rest of this 3x3...

for (row,col) in check_list :

if col not in cols_for_x :

if row not in rows_for_x :

self.cell_exclude(row,col,x)

#Are they all in the same col?

if len(cols_for_x)==1 and len(rows_for_x) > 1 :

#print "%i must be in col %i using rows %s" % (x, cols_for_x[0]+1, ",".join(map(lambda i : str(i+1),rows_for_x)))

#print self

#This means, we can remove X from all the rest of the row...

col = cols_for_x[0]

for row in range(0,9) :

if row not in rows_for_x :

self.cell_exclude(row,col,x)

#We can also remove x from all the rest of this 3x3...

for (row,col) in check_list :

if col not in cols_for_x :

if row not in rows_for_x :

self.cell_exclude(row,col,x)

def one_level_supposition(self):

"""Probably what is known as 'Nishio', try a number and see if it leads to a dead end.

For all the ambigous squares, try each possible each entry and see

if its OK, or if it leads to a contradiction. In the case of a contradiction

we can remove it as a possibility...

Two level suppositions (two guess) may be required for extreme puzzles..."""

progress=True

while progress :

progress=False

#print "Doing one level supposition..."

for row in range(0,9) :

for col in range(0,9):

if len(self.squares[row][col]) > 1 :

bad_x = []

for x in self.squares[row][col] :

#print "/-- Trying setting (%i,%i) to %i" % (row,col,x)

sudoku_copy = self.copy()

try:

sudoku_copy.set_cell(row,col,x)

sudoku_copy.check(level=1)

except AssertionError, e :

#Leads to an error :)

#This means that this square cannot be x

#print e

#print "%s cannot be %i" % (str(self.squares[row][col]), x)

bad_x.append(x)

del sudoku_copy

#print "\-- End of exp"

if len(bad_x) == 0 :

pass

elif len(bad_x) < len(self.squares[row][col]) :

for x in bad_x :

self.cell_exclude(row,col,x)

self.check()

progress=True

else :

assert False, "Bugger! All possible values for square (%i,%i) fail" \

% (row,col)

#print "One level supposition done"

def two_level_supposition(self) :

progress=True

while progress :

progress=False

#print "Doing two level supposition..."

for row in range(0,9) :

for col in range(0,9):

if len(self.squares[row][col]) > 1 :

bad_x = []

for x in self.squares[row][col] :

#print "/-- Trying setting (%i,%i) to %i" % (row,col,x)

sudoku_copy = self.copy()

try:

sudoku_copy.set_cell(row,col,x)

#sudoku_copy.check()

#sudoku_copy.one_level_supposition()

sudoku_copy.check(level=2)

except AssertionError, e :

bad_x.append(x)

del sudoku_copy

#print "\-- End of exp"

if len(bad_x) == 0 :

pass

elif len(bad_x) < len(self.squares[row][col]) :

for x in bad_x :

self.cell_exclude(row,col,x)

self.check()

progress=True

else :

assert False, "Bugger! All possible values for square (%i,%i) fail" \

% (row,col)