Почему этот рекурсивный алгоритм (решатель судоку) возвращает доску в исходное состояние? - PullRequest
0 голосов
/ 19 февраля 2020

Я узнаю о возврате и рекурсии, и я не понимаю (или не могу представить себе в голове), почему мой первоначальный алгоритм выводит плату обратно в исходное состояние вместо печати решения.

Вот моя первая попытка решить проблему:


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


def print_grid(grid):
    rows = len(grid)
    cols = len(grid[0])

    for y in range(rows):
        if y % 3 == 0 and y != 0:
            print("- " * (cols + 3))
        for x in range(cols):
            if x % 3 == 0 and x != 0:
                print(" | ", end="")
            if x == 8:
                print(grid[y][x])
            else:
                print(str(grid[y][x]) + " ", end="")

def find_empty(grid):
    for y in range(9):
        for x in range(9):
            if grid[y][x] == 0:
                return (y, x)


def possible(grid, y, x, n):
    # check rows
    for i in range(9):
        if grid[i][x] == n:
            return False

    # check cols
    for i in range(9):
        if grid[y][i] == n:
            return False

    # check subgrid
    y0 = (y // 3) * 3
    x0 = (x // 3) * 3
    for i in range(3):
        for j in range(3):
            if grid[y0+i][x0+j] == n:
                return False
    return True

def solve(grid):    
    empty = find_empty(grid)
    if not empty:
        return True
    y, x = empty
    for n in range(1, 10):
        if possible(grid, y, x, n):
            grid[y][x] = n
            solve(grid)
            grid[y][x] = 0

solve(grid)
print_grid(grid)

После изменения функции решения на приведенный ниже код я получил ожидаемый результат.

def solve(grid):    
    empty = find_empty(grid)
    if not empty:
        return True
    y, x = empty
    for n in range(1, 10):
        if possible(grid, y, x, n):
            grid[y][x] = n
            # changed this part
            if solve(grid):
                return True
            grid[y][x] = 0

Не первый Достаточно ли точки выхода в функции «если не пусто»?

1 Ответ

3 голосов
/ 19 февраля 2020

Мне также было тяжело оборачивать голову вокруг всей этой сумасшедшей рекурсии, но я как-то сумел понять. Ваша ошибка связана со строкой

grid[y][x] = 0

, потому что это на самом деле перезаписывает правильную доску (попробуйте добавить print_grid(grid) внутри условия if not empty:) после ее решения.

Я думаю, что это скорее понять правильный код, а не понять, почему оригинальный код не работал. Я добавил несколько строк печати, чтобы их было легче понять.

def solve(grid):
    empty = find_empty(grid)
    if not empty:
        print("board completed")
        return True
    y, x = empty
    for n in range(1, 10):
        if possible(grid, y, x, n):
            grid[y][x] = n
            if solve(grid):
                print("board is completed! do not reset anymore")
                return True
            print((y,x), " is not", n, " !!! reset this!!!")
            grid[y][x] = 0

, который печатает:

(0, 4)  is not 9  !!! reset this!!!
(0, 2)  is not 3  !!! reset this!!!
(2, 1)  is not 9  !!! reset this!!!
(2, 0)  is not 3  !!! reset this!!!
(2, 1)  is not 3  !!! reset this!!!
(3, 5)  is not 8  !!! reset this!!!
(3, 3)  is not 1  !!! reset this!!!
(4, 3)  is not 7  !!! reset this!!!
(4, 1)  is not 6  !!! reset this!!!
(4, 0)  is not 2  !!! reset this!!!
(4, 8)  is not 4  !!! reset this!!!
(4, 5)  is not 2  !!! reset this!!!
(4, 3)  is not 7  !!! reset this!!!
(4, 1)  is not 6  !!! reset this!!!
(4, 0)  is not 8  !!! reset this!!!
(3, 8)  is not 1  !!! reset this!!!
(3, 5)  is not 8  !!! reset this!!!
(3, 3)  is not 9  !!! reset this!!!
(3, 1)  is not 5  !!! reset this!!!
(3, 0)  is not 3  !!! reset this!!!
(3, 5)  is not 8  !!! reset this!!!
(3, 3)  is not 1  !!! reset this!!!
(4, 3)  is not 7  !!! reset this!!!
(4, 1)  is not 6  !!! reset this!!!
(4, 0)  is not 2  !!! reset this!!!
(4, 8)  is not 4  !!! reset this!!!
(4, 5)  is not 2  !!! reset this!!!
(4, 3)  is not 7  !!! reset this!!!
(4, 1)  is not 6  !!! reset this!!!
(4, 0)  is not 8  !!! reset this!!!
(3, 8)  is not 1  !!! reset this!!!
(3, 5)  is not 8  !!! reset this!!!
(3, 3)  is not 9  !!! reset this!!!
(3, 1)  is not 3  !!! reset this!!!
(3, 0)  is not 5  !!! reset this!!!
(3, 3)  is not 1  !!! reset this!!!
(3, 3)  is not 9  !!! reset this!!!
(3, 1)  is not 3  !!! reset this!!!
(3, 5)  is not 3  !!! reset this!!!
(3, 3)  is not 1  !!! reset this!!!
(6, 5)  is not 4  !!! reset this!!!
(6, 4)  is not 8  !!! reset this!!!
(7, 3)  is not 5  !!! reset this!!!
(7, 2)  is not 8  !!! reset this!!!
(6, 6)  is not 8  !!! reset this!!!
(6, 5)  is not 4  !!! reset this!!!
(6, 4)  is not 9  !!! reset this!!!
(6, 2)  is not 6  !!! reset this!!!
board completed

в основном, not empty будет истинным, только если доска заполнена правильно , Потому что, если какой-либо номер находится в неправильной позиции, в конечном итоге этот неправильный номер будет сброшен на grid[y][x] = 0 и протестирован с другим номером.

Если доска завершена и if not empty: return True была выполнена, if solve(grid): будет знать, что она больше не должна сбрасывать доску, поэтому возвращает True. Это выйдет из функции, что означает, что строка grid[y][x] = 0 будет пропущена.

Если вы не понимаете, попробуйте печатать между каждой строкой, чтобы вы могли понять, что происходит. Надеюсь, это помогло:)

...