Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何改进这个数独解算器?_Python_Python 3.x_Performance_Recursion_Sudoku - Fatal编程技术网

Python 如何改进这个数独解算器?

Python 如何改进这个数独解算器?,python,python-3.x,performance,recursion,sudoku,Python,Python 3.x,Performance,Recursion,Sudoku,不久前我有一个想法,制作一个程序来解决数独板问题,所以我制作了下面的代码。代码接收一个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 positi

不久前我有一个想法,制作一个程序来解决数独板问题,所以我制作了下面的代码。代码接收一个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)
我的问题是:如果函数
数独
接收到以下列表作为输入

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]]
它在不到一秒钟的时间内返回结果,即

[[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]]
但如果它接收到列表作为输入:

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]]
它应该会回来

[[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]]
但是程序运行时间太长了,我甚至不知道它是否返回了什么(在结束代码执行之前我等了30分钟)。因此,我的疑问是:

  • 我的代码中是否存在某些输入类型的错误
  • 如何改进我的代码以接受包含更多空单元格的条目
  • 我的代码运行得非常好,对于空单元格较多的条目,花费较长的时间是正常的吗

谢谢你的帮助

正如我所评论的,这是一个很难的数独游戏,所以你必须猜几个单元格才能解决它。您可以查看我不久前编程的硬数独解算器,如果有帮助的话:

def数独(网格):
数独游戏
r='ABCDEFGHI'
c='123456789'
对于范围(9)内的i:
对于范围(9)内的j:
数独[r[i]+c[j]=str(grid[i][j])如果grid[i][j]!=0其他c
平方=[[x+y代表x in i代表y in j]代表i in('ABC','DEF','GHI'),代表j in('123','456','789')]
对等体={}
对于数独中的键(dict.keys():
值=[i为i,如果输入i][0]
行=[[x+y代表x在i中代表y在j中][0]代表i在键[0]中代表j在c中]
col=[[x+y代表x在i中代表y在j中][0]代表i在r中代表j在键[1]]
对等点[键]=设置(如果x!=键,则x代表x的值+行+列)
对于范围(9)内的i:
数独=检查(数独,同级)
数独游戏=搜索(数独游戏,同级)
解决方案=[]
对于r中的i:
解决方案。追加([]))
对于c中的j:
解[r.find(i)].append(int(数独[i+j]))
返回溶液
def检查(数独、同级):
对于数独中的k,v,dict.items():
如果len(v)==1:
对于对等方中的s[k]:
数独[s]=数独[s]。替换(v,,)
如果len(数独)=0:
返回错误
返回数独词典
def搜索(数独、同级):
如果检查(数独词典,对等体)=错误:
返回错误
如果所有(len(数独[s])==1表示数独键()中的s):
返回数独词典
n、 s=s的最小值((len(sudoku_dict[s]),s)表示数独键中的s(),如果len(sudoku_dict[s])>1)
res=[]
对于数独游戏中的值:
new_sudoku_dict=sudoku_dict.copy()
新数独dict[s]=值
ans=搜索(新数独词典,同级)
如果答案是:
附加资源(ans)
如果len(res)>1:
引发异常(“错误”)
elif len(res)==1:
返回res[0]

正如我所评论的,这是一个很难的数独游戏,所以你必须猜几个单元格才能解决它。您可以查看我不久前编程的硬数独解算器,如果有帮助的话:

def数独(网格):
数独游戏
r='ABCDEFGHI'
c='123456789'
对于范围(9)内的i:
对于范围(9)内的j:
数独[r[i]+c[j]=str(grid[i][j])如果grid[i][j]!=0其他c
平方=[[x+y代表x in i代表y in j]代表i in('ABC','DEF','GHI'),代表j in('123','456','789')]
对等体={}
对于数独中的键(dict.keys():
值=[i为i,如果输入i][0]
行=[[x+y代表x在i中代表y在j中][0]代表i在键[0]中代表j在c中]
col=[[x+y代表x在i中代表y在j中][0]代表i在r中代表j在键[1]]
对等点[键]=设置(如果x!=键,则x代表x的值+行+列)
对于范围(9)内的i:
数独=检查(数独,同级)
数独游戏=搜索(数独游戏,同级)
解决方案=[]
对于r中的i:
解决方案。追加([]))
对于c中的j:
解[r.find(i)].append(int(数独[i+j]))
返回溶液
def检查(数独、同级):
对于数独中的k,v,dict.items():
如果len(v)==1:
对于对等方中的s[k]:
数独[s]=数独[s]。替换(v,,)
如果len(数独)=0:
返回错误
返回数独词典
def搜索(数独、同级):
如果检查(数独词典,对等体)=错误:
返回错误
如果所有(len(数独[s])==1表示数独键()中的s):
返回数独词典
n、 s=s的最小值((len(sudoku_dict[s]),s)表示数独键中的s(),如果len(sudoku_dict[s])>1)
res=[]
对于数独游戏中的值:
new_sudoku_dict=sudoku_dict.copy()
新数独dict[s]=值
ans=搜索(新数独词典,同级)
如果答案是:
附加资源(ans)
如果len(res)>1:
引发异常(“错误”)
elif len(res)==1:
返回res[0]

通过在嵌套循环末尾的
数独()函数中添加
return
语句,可以让程序解决第二个难题。下面的代码提供了该修复和其他一些返工想法:

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)
输出

> 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]]
>
你的解算器是一个蛮力解算器,它对游戏本身使用了很少的智能。所以,我不能保证不会再有一个需要很长时间才能完成的谜题。一个更高效的解算器可能会尝试人类在诉诸暴力之前放置数字的所有技巧


我所做的修改可能会阻止您的代码找到多个解决方案(如果存在)。

您可以通过在嵌套循环末尾的
sudoku()
函数中添加
return
语句,让您的程序解决第二个难题。下面的代码提供了该修复和其他一些返工想法:

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)
输出

> 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]]
>
你的解算器是一个蛮力解算器,它对游戏本身使用了很少的智能。所以,我不能保证不会再有一个需要很长时间才能完成的谜题。一个更高效的解算器可能会尝试所有的技巧