使用「暴力法」解數獨 - tsungjung411/python-study GitHub Wiki
- https://leetcode.com/problems/sudoku-solver/ 37. Sudoku Solver
- Content:
- Write a program to solve a Sudoku puzzle by filling the empty cells.
- A sudoku solution must satisfy all of the following rules:
- Each of the digits 1-9 must occur exactly once in each row.
- Each of the digits 1-9 must occur exactly once in each column.
- Each of the the digits 1-9 must occur exactly once in each of the 9 3x3 sub-boxes of the grid.
- Empty cells are indicated by the character '.'
board1 = [["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]
answer = [["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]
- 此 case 無法使用消去法,需使用暴力法下去搜尋
board2 = [[".",".","9","7","4","8",".",".","."],["7",".",".",".",".",".",".",".","."],[".","2",".","1",".","9",".",".","."],[".",".","7",".",".",".","2","4","."],[".","6","4",".","1",".","5","9","."],[".","9","8",".",".",".","3",".","."],[".",".",".","8",".","3",".","2","."],[".",".",".",".",".",".",".",".","6"],[".",".",".","2","7","5","9",".","."]]
answer = [["5","1","9","7","4","8","6","3","2"],["7","8","3","6","5","2","4","1","9"],["4","2","6","1","3","9","8","7","5"],["3","5","7","9","8","6","2","4","1"],["2","6","4","3","1","7","5","9","8"],["1","9","8","5","2","4","3","6","7"],["9","7","5","8","6","3","1","2","4"],["8","3","2","4","9","1","7","5","6"],["6","4","1","2","7","5","9","8","3"]]
主要分成兩大塊:
-
SudokuSolver 類別。 不管是消去法、暴力法、還是其他方法,SudokuSolver 的實作就是他們的共同核心
- init_states_and_get_empty_cell_list(self, board): 初始化已經佔用的數值,並回傳空的位置
- is_accepted(self, row, col, digit): 該數字在指定位置是否能被接受?
- set_cell(self, row, col, digit): 在指定的位置上,佔用該數值
- unset_cell(self, row, col, digit): 在指定的位置上數值,取消佔用該數值
- __repr__(self): 印出棋盤
-
BruteForce 類別,則是繼承自 SudokuSolver 類別。
- _traversal(self, idx): 實作遞迴拜訪,直到填滿所有格子後就返回
class SudokuSolver:
'''
Implements common methods.
'''
def __init__(self, board):
self.int_board = [[0] * 9 for _ in range(9)]
# -----------------------------------------------
# for the tables below,
# we ignores the digit 0 of the digit list [0~9]
# -----------------------------------------------
# horizontal states: [row_index][1~9]
self.row_states = [[False] * 10 for _ in range(9)]
# vertical states: [col_index][1~9]
self.col_states = [[False] * 10 for _ in range(9)]
# block states: [block_row_index][block_col_index][1~9]
self.block_states = [
[
[False] * 10 for _ in range(3) # for cols
] for _ in range(3) # for rows
]
# end-of-def
def init_states_and_get_empty_cell_list(self, board):
empty_cell_list = list()
# (1) collects the empty cells
# (2) marks the states of the non-empty cells as occupied
for row in range(9):
for col in range(9):
if board[row][col] == '.':
digit_set = set()
cell = (row, col, digit_set)
empty_cell_list.append(cell)
else:
digit = int(board[row][col])
self.set_cell(row, col, digit)
# end-of-if
# end-of-for
# end-of-for
for row, col, digit_set in empty_cell_list:
for digit in range(1, 10):
if self.is_accepted(row, col, digit):
digit_set.add(digit)
# end-of-if
# end-of-for
# shows possible digits for empty celss
#print('[{}][{}]={}'.format(row, col, digit_set))
# end-of-for
return empty_cell_list
# end-of-def
def is_accepted(self, row, col, digit):
'''
Indicates where the digit on the board (row, col) is accepted.
'''
if self.row_states[row][digit]:
return False
elif self.col_states[col][digit]:
return False
else:
block_row = row // 3
block_col = col // 3
if self.block_states[block_row][block_col][digit]:
return False
# end-of-if
# end-of-if
return True
# end-of-def
def set_cell(self, row, col, digit):
self.int_board[row][col] = digit
self.row_states[row][digit] = True
self.col_states[col][digit] = True
self.block_states[row//3][col//3][digit] = True
# end-of-def
def unset_cell(self, row, col, digit):
self.int_board[row][col] = 0
self.row_states[row][digit] = False
self.col_states[col][digit] = False
self.block_states[row//3][col//3][digit] = False
# end-of-def
def __repr__(self):
s = '-' * 13 + '\n'
for r in range(9):
s += '|'
for c in range(9):
digit = self.int_board[r][c]
s += str(digit) if digit != 0 else ' '
if c == 2 or c == 5 or c == 8:
s += '|'
# end-of-if
# end-of-for
s += '\n'
if r == 2 or r == 5 or r == 8:
s += '-' * 13 + '\n'
# end-of-if
# end-of-for
return s
# end-of-def
# end-of-class
class BruteForce(SudokuSolver):
'''
Implements the brute-force method for Sudoku.
'''
def __init__(self, board):
super(BruteForce, self).__init__(board)
self.empty_cell_list = self.init_states_and_get_empty_cell_list(board)
self.empty_cell_list.sort(
key = lambda cell : len(cell[2]) * 100 + cell[0] * 10 + cell[1])
# end-of-def
def run(self):
self.found = False
self._traversal(0)
# end-of-def
def _traversal(self, idx):
if idx == len(self.empty_cell_list):
self.found = True
return True
# end-of-if
#print(self)
row, col, digit_set = self.empty_cell_list[idx]
for digit in digit_set:
if self.is_accepted(row, col, digit):
self.set_cell(row, col, digit)
# fills-in the next digit
self._traversal(idx + 1)
if not self.found:
self.unset_cell(row, col, digit)
# end-of-if
# end-of-if
# end-of-for
return False
# end-of-def
# end-of-class
def main():
board1 = [
["5","3",".",".","7",".",".",".","."],
["6",".",".","1","9","5",".",".","."],
[".","9","8",".",".",".",".","6","."],
["8",".",".",".","6",".",".",".","3"],
["4",".",".","8",".","3",".",".","1"],
["7",".",".",".","2",".",".",".","6"],
[".","6",".",".",".",".","2","8","."],
[".",".",".","4","1","9",".",".","5"],
[".",".",".",".","8",".",".","7","9"]]
board2 = [
[".",".","9","7","4","8",".",".","."],
["7",".",".",".",".",".",".",".","."],
[".","2",".","1",".","9",".",".","."],
[".",".","7",".",".",".","2","4","."],
[".","6","4",".","1",".","5","9","."],
[".","9","8",".",".",".","3",".","."],
[".",".",".","8",".","3",".","2","."],
[".",".",".",".",".",".",".",".","6"],
[".",".",".","2","7","5","9",".","."]]
board = board1
e = BruteForce(board)
print(e)
e.run()
print(e)
# end-of-def
main()
board1 執行結果:
-------------
|53 | 7 | |
|6 |195| |
| 98| | 6 |
-------------
|8 | 6 | 3|
|4 |8 3| 1|
|7 | 2 | 6|
-------------
| 6 | |28 |
| |419| 5|
| | 8 | 79|
-------------
-------------
|534|678|912|
|672|195|348|
|198|342|567|
-------------
|859|761|423|
|426|853|791|
|713|924|856|
-------------
|961|537|284|
|287|419|635|
|345|286|179|
-------------
board2 執行結果:
-------------
| 9|748| |
|7 | | |
| 2 |1 9| |
-------------
| 7| |24 |
| 64| 1 |59 |
| 98| |3 |
-------------
| |8 3| 2 |
| | | 6|
| |275|9 |
-------------
-------------
|519|748|632|
|783|652|419|
|426|139|875|
-------------
|357|986|241|
|264|317|598|
|198|524|367|
-------------
|975|863|124|
|832|491|756|
|641|275|983|
-------------