【问题标题】:Python backtracing algorithm to solve sudokusPython回溯算法解决数独问题
【发布时间】:2019-03-01 20:25:51
【问题描述】:

刚从一些python代码开始,我试图使用here发布的回溯算法制作数独求解器

所以我现在有这个:

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

     sublist = []

def validate(value, y, x, sudoku):

    # check linea
    for idx in range(0,9):
        if sudoku[y][idx] == value:
            return False   
    for idy in range(0, 9):
        if sudoku[idy][x] == value:
            return False
    return True


def validate_square(value, y, x, sudoku):

    if y < 3 and x < 3:
        aux_list = [aux_list[0:3] for aux_list in sudoku[0:3]]
        if value in [j for i in aux_list for j in i]:
            return False
        else:
            return True

    elif y < 3 and (x >= 3 and x < 6):
        aux_list = [aux_list[3:6] for aux_list in sudoku[0:3]]
        if value in [j for i in aux_list for j in i]:
            return False
        else:
            return True

    elif y < 3 and (x >= 6 and x < 9):
        aux_list = [aux_list[6:9] for aux_list in sudoku[0:3]]
        if value in [j for i in aux_list for j in i]:
            return False
        else:
            return True

    elif (y >= 3 and y < 6) and x < 3:
        aux_list = [aux_list[0:3] for aux_list in sudoku[3:6]]
        if value in [j for i in aux_list for j in i]:
            return False
        else:
            return True

    elif (y >= 3 and y < 6) and (x >= 3 and x < 6):
        aux_list = [aux_list[3:6] for aux_list in sudoku[3:6]]
        if value in [j for i in aux_list for j in i]:
            return False
        else:
            return True

    elif (y >= 3 and y <6) and (x >= 6 and x < 9):
        aux_list = [aux_list[6:9] for aux_list in sudoku[3:6]]
        if value in [j for i in aux_list for j in i]:
            return False
        else:
            return True

    elif (y >= 6 and y < 9) and x < 3:
        aux_list = [aux_list[0:3] for aux_list in sudoku[6:9]]
        if value in [j for i in aux_list for j in i]:
            return False
        else:
            return True

    elif (y >= 6 and y < 9) and (x >= 3 and x < 6):
        aux_list = [aux_list[3:6] for aux_list in sudoku[6:9]]
        if value in [j for i in aux_list for j in i]:
            return False
        else:
            return True

    elif (y >= 6 and y < 9) and (x >= 6 and x < 9):
        aux_list = [aux_list[6:9] for aux_list in sudoku[6:9]]
        if value in [j for i in aux_list for j in i]:
            return False
        else:
            return True
    else:
        return True

def auto_complete(sudoku):
    for idy in range(0,9):
        for idx in range(0,9):
            if sudoku[idy][idx] == 0:
                for attempt in range(1, 10):
                    if validate(attempt, idy, idx, sudoku) == True and validate_square(attempt, idy, idx, sudoku) == True:
                        sudoku[idy][idx] = attempt

if __name__ == "__main__":
    auto_complete(SUDOKU)
    for i in SUDOKU:
        print(i)

这是我得到的解决方案:

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

这是预期的一个(正确的解决方案): sudoku

此时我有一个算法可以部分回溯,但是当涉及到一个不适合其单元格(行、行、方形违规)的值时,它只会将其留空,从*帖子我知道我应该去向后的位置(可能是从 idx 中减去 1?)将 1 添加到该单元格并重新验证该值。

我很迷茫,所以关于如何以 Python 方式改进此代码的任何建议?

另外,列表列表是构建数独数据的最佳方式吗?它们的工作方式似乎与数组有点不同(这是我来自的地方,反正不是专家)

感谢大家的阅读!

【问题讨论】:

  • 我看起来不像你的 auto_complete 函数做*中描述的回溯,如果我理解正确的话,因为如果一个单元格不能被验证,你必须后退,你在这里不这样做。所以要从一个单元格导航到另一个单元格,我会做一个 while 循环,如果一个单元格无法验证,我会减少 idx 或 idy。
  • 回溯算法是一种广度优先搜索算法,这意味着它在技术上创建一个树/图数据结构,因为它填充每个正方形。当 a 出现错误值时,它会跳回到树中最正确的最低点。可以在方形背面或多行背面。您可能必须存储所有以前访问过的棋盘状态才能知道要回溯多远。等离子方法也可以。时间复杂度更差,但更容易编码。如果将它们插入到树结构中,则还必须制作树。
  • 我想了想,这可能是我尝试解决这个问题的方式,但是,在嵌套的 for 循环中运行一段时间的性能如何?我的意思是,情况并非如此,因为两个 fors 都很小,但这是一个好的做法还是建议从其他方式接近?
  • 另外,在您的代码中,您选择就地修改数独。如果这样做,在回溯时,您必须小心不要更改在数独开始时设置的值。
  • norvig.com/sudoku.html 这可能会有所帮助。他概述了一个关于编程蛮力解决方案的从头到尾的教程

标签: python backtracking sudoku


【解决方案1】:

我想指出“验证方”的更好解决方案是:

square_x = x // 3 * 3
square_y = y // 3 * 3
for row in range(square_x, square_x + 3):
    for col in range(square_y, square_y + 3):
        if num == main_grid[row][col]:
            print(f"You can't put {num} in {x}|{y}.\n"
                  f"{num} already exists in {row}|{col}. "
                  f"[square: {x // 3}|{y // 3}]")
            return False

数独网格可以分成 9 个方格;)

【讨论】: