简体   繁体   English

如何改善数独解算器?

[英]How to improve this Sudoku Solver?

I had an idea a while ago of making a program that would solve a Sudoku board, so I made the code below. 前段时间我有一个想法,想编写一个可以解决Sudoku电路板的程序,所以我在下面编写了代码。 The code receives as input a 9x9 integer list, where an incomplete cell is represented by the number 0. 该代码接收9x9整数列表作为输入,其中不完整的单元格由数字0表示。

def checkSolutions(grid, i, j):
    """
    Given a Sudoku board and the position of an
    incomplete cell, it returns a list with all
    the possible numbers that this position can occupy.
    """

    digits = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    solutions = []

    solutions1x9 = [grid[x][j] for x in range(9)]
    solutions9x1 = [grid[i][x] for x in range(9)]

    rowGrid = i // 3
    columnGrid = j // 3
    solutions3x3 = [grid[i][j] for i in range(3*rowGrid, 3*rowGrid+3)
                    for j in range(3*columnGrid, 3*columnGrid+3)]

    solutions = solutions + [i for i in digits if i not in solutions1x9]
    solutions = solutions + [i for i in digits if i not in solutions9x1]
    solutions = solutions + [i for i in digits if i not in solutions3x3]

    solutions = list(set(solutions))
    solutions = [i for i in solutions if i not in solutions1x9]
    solutions = [i for i in solutions if i not in solutions9x1]
    solutions = [i for i in solutions if i not in solutions3x3]

    return solutions

def checkSudoku(grid):
    """
    Given a Sudoku board, it returns True if it is
    a board that follows the rules of the game and
    returns False otherwise.
    """

    digits = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    for i in range(9):
        if sorted(grid[i]) != digits:
            return False

    for i in range(9):
        column = [grid[j][i] for j in range(9)]
        if sorted(column) != digits:
            return False

    for i in range(3):
        for j in range(3):
            grid3x3 = [grid[x][y] for x in range(3*i, 3*i+3)
                    for y in range(3*j, 3*j+3)]
            if sorted(grid3x3) != digits:
                return False

    return True

def sudoku(grid):
    """
    Given an incomplete Sudoku board, it prints on
    the screen the solution of that game.
    """
    for i in range(9):
        for j in range(9):
            if grid[i][j] == 0:
                solutions = checkSolutions(grid, i, j)

                if len(solutions) == 1:
                    grid[i][j] = solutions[0]
                    continue

                for k in solutions:
                    auxGrid = [x.copy() for x in grid]
                    auxGrid[i][j] = k
                    sudoku(auxGrid)

    if checkSudoku(grid):
        print(grid)

My problem is: if the function sudoku receives as input the following list 我的问题是:如果sudoku功能接收到以下列表作为输入

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

it returns the result in less than one second, which is 它会在不到一秒的时间内返回结果,即

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

But if it receives as input the list: 但是,如果它收到输入作为清单:

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

it should returns 它应该返回

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

but the program takes so long to run that I don't even know if it returns something (I waited 30 minutes before closing the code execution). 但是该程序要花很长时间才能运行,我什至不知道它是否返回任何东西(我等了30分钟才关闭代码执行)。 So my doubts are: 所以我的疑问是:

  • is there a mistake in my code for certain input types? 我的代码中某些输入类型有错误吗?
  • how can I improve my code to accept entries with more empty cells? 如何改善代码以接受带有更多空单元格的条目?
  • my code works perfectly fine and is it normal to take longer for entries with more empty cells? 我的代码工作得很好,是否有更长的时间输入带有更多空单元格的条目,这正常吗?

Thanks for any help! 谢谢你的帮助!

You can get your program to solve the second puzzle by adding a return statement to your sudoku() function at the end of the nested loops. 通过在嵌套循环的末尾向sudoku()函数添加return语句,可以使程序解决第二个难题。 The code below has that fix and some other rework ideas: 下面的代码具有修复程序和其他一些返工思路:

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

def checkSolutions(grid, i, j):
    """
    Given a Sudoku board, and the position of an
    incomplete cell, it returns a list with all
    the possible numbers that can occupy this position.
    """

    solutions1x9 = [grid[x][j] for x in range(9)]
    solutions9x1 = [grid[i][x] for x in range(9)]

    rowGrid = 3 * (i // 3)
    columnGrid = 3 * (j // 3)
    solutions3x3 = [grid[i][j] for i in range(rowGrid, rowGrid + 3) for j in range(columnGrid, columnGrid + 3)]

    return [digit for digit in DIGITS if digit not in solutions1x9 and digit not in solutions9x1 and digit not in solutions3x3]

def checkSudoku(grid):
    """
    Given a Sudoku board, it returns True if it is
    a board that follows the rules of the game and
    returns False otherwise.
    """

    for i in range(9):
        if sorted(grid[i]) != DIGITS:
            return False

    for j in range(9):
        column = [grid[i][j] for i in range(9)]

        if sorted(column) != DIGITS:
            return False

    for i in range(3):
        for j in range(3):
            grid3x3 = [grid[x][y] for x in range(3 * i, 3 * i + 3) for y in range(3 * j, 3 * j + 3)]

            if sorted(grid3x3) != DIGITS:
                return False
    return True

def sudoku(grid):
    """
    Given an incomplete Sudoku board, it prints on
    the screen the solution of that game.
    """

    for i in range(9):
        for j in range(9):
            if grid[i][j] == 0:
                solutions = checkSolutions(grid, i, j)

                if len(solutions) == 1:
                    grid[i][j] = solutions[0]  # permanent change to *this* reality
                    continue

                for k in solutions:
                    auxGrid = [x.copy() for x in grid]  # spawn a new reality
                    auxGrid[i][j] = k
                    sudoku(auxGrid)

                return  # already solved it recursively or no solution in *this* reality

    if checkSudoku(grid):
        print(grid)

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

sudoku(grid2)

OUTPUT OUTPUT

> python3 test.py
[[1, 6, 8, 4, 5, 7, 9, 3, 2],
 [5, 7, 2, 3, 9, 1, 4, 6, 8],
 [9, 3, 4, 6, 2, 8, 5, 1, 7],
 [8, 2, 9, 7, 4, 3, 1, 5, 6],
 [6, 5, 1, 2, 8, 9, 3, 7, 4],
 [7, 4, 3, 5, 1, 6, 2, 8, 9],
 [3, 9, 5, 8, 7, 2, 6, 4, 1],
 [4, 1, 7, 9, 6, 5, 8, 2, 3],
 [2, 8, 6, 1, 3, 4, 7, 9, 5]]
>

Your solver is a brute-force solver which uses few smarts about the game itself. 您的求解器是一种蛮力求解器,它很少使用有关游戏本身的技巧。 So, I can't promise there won't be a puzzle that again takes too long to finish. 因此,我不能保证不会再花太长时间才能完成。 A more efficient solver might try all the tricks a human would to place digits before resorting to brute-force. 一个更高效的求解器可能会尝试使用蛮力之前人类放置数字的所有技巧。

The modification I made may prevent your code from finding multiple solutions if they exist. 我所做的修改可能会阻止您的代码找到多个解决方案(如果存在)。

As I commented, this is a hard sudoku, so you have to guess several cells to be able to solve it. 正如我评论的那样,这是一个很难的数独,因此您必须猜测几个单元才能解决它。 You can check my hard sudoku solver I programmed some time ago, if that helps: 您可以检查一下我之前编写的硬数独求解器,如果有帮助的话:

def sudoku(grid):
    sudoku_dict = {}
    r = 'ABCDEFGHI'
    c = '123456789'
    for i in range(9):
        for j in range(9):
            sudoku_dict[r[i]+c[j]] = str(grid[i][j]) if grid[i][j] != 0 else c
    square = [[x+y for x in i for y in j] for i in ('ABC','DEF','GHI') for j in ('123','456','789')]
    peers = {}
    for key in sudoku_dict.keys():
        value = [i for i in square if key in i][0]
        row = [[x+y for x in i for y in j][0] for i in key[0] for j in c]
        col = [[x+y for x in i for y in j][0] for i in r for j in key[1]]
        peers[key] = set(x for x in value+row+col if x != key)
    for i in range(9):
        sudoku_dict = Check(sudoku_dict,peers)
    sudoku_dict = search(sudoku_dict, peers)
    solution = []
    for i in r:
        solution.append([])
        for j in c:
            solution[r.find(i)].append(int(sudoku_dict[i+j]))
    return solution

def Check(sudoku_dict, peers):
    for k,v in sudoku_dict.items():
        if len(v) == 1:
            for s in peers[k]:
                sudoku_dict[s] = sudoku_dict[s].replace(v,'')
                if len(sudoku_dict[s])==0:
                    return False
    return sudoku_dict

def search(sudoku_dict,peers):
    if Check(sudoku_dict,peers)==False:
        return False
    if all(len(sudoku_dict[s]) == 1 for s in sudoku_dict.keys()): 
        return sudoku_dict
    n,s = min((len(sudoku_dict[s]), s) for s in sudoku_dict.keys() if len(sudoku_dict[s]) > 1)
    res = []
    for value in sudoku_dict[s]:
        new_sudoku_dict = sudoku_dict.copy()
        new_sudoku_dict[s] = value
        ans = search(new_sudoku_dict, peers)
        if ans:
            res.append(ans)
    if len(res) > 1:
        raise Exception("Error")
    elif len(res) == 1:
        return res[0]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM