Программа курса:
Разбор задачи: Решение судоку
Предложенный нами код:
def solveSudoku(board):
"""
Решает задачу судоку путем заполнения пустых клеток.
:param board: List[List[str]] - доска судоку 9x9
:return: None - модифицирует переданный `board` на месте
"""
def is_valid(num, row, col):
# Проверяем строку и столбец
for i in range(9):
if board[row][i] == num or board[i][col] == num:
return False
# Проверяем 3x3 подблок
start_row, start_col = 3 * (row // 3), 3 * (col // 3)
for i in range(start_row, start_row + 3):
for j in range(start_col, start_col + 3):
if board[i][j] == num:
return False
return True
def backtrack():
for row in range(9):
for col in range(9):
if board[row][col] == ".":
for num in map(str, range(1, 10)):
if is_valid(num, row, col):
board[row][col] = num
if backtrack():
return True
board[row][col] = "."
return False
return True
backtrack()Решение задачи основано на использовании метода проб и ошибок (backtracking), который проходит по всем возможным значениям для пустых клеток и проверяет их валидность, чтобы заполнить всю доску судоку. Рассмотрим основные этапы решения и разберем код фрагментами.
1. Входные данные и задача функции
Функция solveSudoku принимает на вход игровую доску судоку в виде 2D-списка board, где каждая клетка представлена символом:
- Числа от
'1'до'9'обозначают заполненные клетки. - Символ
'.'обозначает пустую клетку.
Функция модифицирует переданный board так, чтобы в нем осталась единственная корректная конфигурация судоку.
2. Проверка валидности числа
def is_valid(num, row, col):
for i in range(9):
if board[row][i] == num or board[i][col] == num:
return False
start_row, start_col = 3 * (row // 3), 3 * (col // 3)
for i in range(start_row, start_row + 3):
for j in range(start_col, start_col + 3):
if board[i][j] == num:
return False
return True
Этот вспомогательный метод проверяет, можно ли разместить число num в клетке с координатами (row, col):
- Сначала проверяются строки и столбцы. Если число уже присутствует в строке или столбце, возвращается
False. - Затем проверяется 3x3 подблок, в котором находится клетка. Если число уже присутствует в этом подблоке, возвращается
False. - Если все проверки пройдены, возвращается
True.
Пример: Если мы хотим вставить '5' в клетку (0, 2), проверяются:
- Все числа в строке
0. - Все числа в столбце
2. - Все числа в 3x3 блоке, который охватывает
(0, 2).
3. Реализация backtracking
def backtrack():
for row in range(9):
for col in range(9):
if board[row][col] == ".":
for num in map(str, range(1, 10)):
if is_valid(num, row, col):
board[row][col] = num
if backtrack():
return True
board[row][col] = "."
return False
return True
Эта функция реализует метод проб и ошибок. Рассмотрим его работу:
- Поиск пустой клетки:
- Вложенные циклы перебирают все клетки доски.
- Если текущая клетка пустая (
"."), начинается процесс заполнения.
- Подстановка возможных значений:
- Функция перебирает числа от
'1'до'9'. - Для каждого числа проверяется его валидность с помощью функции
is_valid.
- Функция перебирает числа от
- Рекурсивный вызов:
- Если число валидно, оно временно размещается в клетке, и выполняется рекурсивный вызов
backtrackдля проверки следующей пустой клетки. - Если размещение приводит к успешному решению, функция возвращает
True.
- Если число валидно, оно временно размещается в клетке, и выполняется рекурсивный вызов
- Откат (backtracking):
- Если дальнейшее решение невозможно, число удаляется из клетки, и продолжается перебор оставшихся чисел.
- Если никакое число не подходит, функция возвращает
False, сигнализируя об откате на предыдущий шаг.
4. Основная функция
def solveSudoku(board):
backtrack()
Функция solveSudoku вызывает рекурсивную функцию backtrack, которая находит решение судоку. После выполнения, переданный board содержит корректное заполнение.
Пошаговый пример работы кода
Рассмотрим входную доску:
board = [
["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"]
]
- Поиск пустой клетки:
- Первая пустая клетка находится в позиции
(0, 2).
- Первая пустая клетка находится в позиции
- Проба чисел:
- Число
'1'проверяется черезis_validи оказывается некорректным (уже есть в строке). - Число
'4'оказывается корректным и временно размещается в клетке.
- Число
- Рекурсия:
- Вызов
backtrackпродолжается с новой доской. - Аналогичные шаги повторяются для следующей пустой клетки.
- Вызов
- Откат:
- Если размещение числа в какой-либо клетке приводит к тупиковой ситуации (нет решений для следующей пустой клетки), выполняется откат: число удаляется, и продолжается перебор оставшихся чисел.
- Завершение:
- После успешного заполнения всех клеток функция возвращает
True.
- После успешного заполнения всех клеток функция возвращает