Python递归数独求解器

时间:2015-07-28 13:01:15

标签: python algorithm function recursion graph-algorithm

这是recursive Sudoku solve代码,这不是学校作业。我无法弄清楚如何让它回归"备份"通过我以前的动作。它会在first row结束时卡住,因为该地点没有有效数字,只是不断尝试找到适合那里的数字。我遇到问题的功能是check

在阅读了你的答案后,我已经接近了,但它并不完全存在。它一直支持并退出递归

import sys

class Board:

    def __init__(self, grid):
        self.grid = grid
        self.ogrid = grid

    def get_col(self, col):
        column = []
        for i in self.grid:
           column.append(str(i[col]))
        return column

    def get_row(self, row):
        return self.grid[row]

    def check_row(self, r, val):
        row = self.grid[r]
        for i in range(0,9):
            if str(val) in row:
                return False

        return True

    def check_col(self, column, val):
        col = self.get_col(column)
        for i in range(0,9):
            if str(val) in col:
                return False

        return True

    def check_square(self, x, y, val):
        col = (y//3)*3
        row = (x//3)*3
        s = ''
        for i in range(row, row+3):
            for j in range(col, col+3):
                s += str(self.grid[i][j])
        if str(val) in s:
                return False

        return True       

    def check_cell(self, x, y, val):
    if self.check_col(y, val) == False:

        return False
    elif self.check_row(x, val) == False:

        return False
    elif self.check_square(x, y, val) == False:

        return False
    return True


def check(self, x, y):

        if y == 9:
            y = 0
            x += 1

        if x == 9:  
            self.print_board()
            sys.exit()

        if self.ogrid[x][y] == '.':
            for val in range(1,10):
                if self.check_cell(x, y, val): 
                    self.grid[x][y] = str(val)
                    self.check(x, y+1)
                ##I don't think the reset is working and I'm not sure why
                if self.ogrid[x][y] == '.': #reset index
                    self.grid[x][y] = '.'
                    self.print_board() #Notice it never prints a '.' in spots that have changed
        else:
            self.check(x,y+1)
        return

    def print_board(self):
        for i in range(0,9):
            for j in range(0,9):
                sys.stdout.write(self.grid[i][j])
                if j == 2 or j == 5:
                    sys.stdout.write(' ')
            if i == 2 or i == 5:
                sys.stdout.write('\n')
            print('')

def main():

    f = open("./easySudoku.txt",'r')
    s = ''

    grid = []
    row = []
    for line in f:
        s += line
        print line
    s = s.replace(' ','')
    s = s.replace('\n','')

    for i in range(0,9):
        row = []
        for j in range(0,9):
            row.append(s[(i*9) + j])
        grid.append(row)

    sudoku = Board(grid)
    sudoku.check(0,0, 1)

if __name__ == "__main__":
    main()

以下是检查功能应如何工作

  

check获取棋盘的x和y坐标,并从0,0开始,它从1-9开始运行for循环,并设置在该索引处起作用的第一个值,然后移动到下一个索引。当它到达行的末尾时,它向下移动一行并返回到第一列。如果没有值在索引处起作用,则将当前索引重置为'.'并将索引向后移动一个并继续计数到9。即如果索引0,0的当前值为2,则继续3。如果3有效,则向前移动一个索引,依此类推,直到董事会被填满为止。对于简单的答案,它会在每个空索引

处尝试每个值1-9

如果不清楚,请告诉我

此处是我正在使用的电路板

..6 ..7 3..
.18 ..9 .5.
5.. ... .64

92. .8. ...
... 763 ...
... .9. .75

63. ... ..8
.9. 3.. 52.
..2 4.. 6..

4 个答案:

答案 0 :(得分:2)

问题是你似乎为你尝试的每个数字递归一步,这将消耗每个理智的堆栈。我建议您也使用迭代并使用return进行备份(这样你应该只使用81个堆栈帧 - 这样当你获得一千个堆栈帧时它会失败)。

我以前做过求解器,它会很快找到解决方案......

答案 1 :(得分:1)

我为检查您的代码所做的工作:将此行添加为check方法的第一行:

raw_input('x: %d, y: %d, val: %d' % (x,y,val))

并在插入数字后打印电路板。

看起来你的求解器在(x,y) = (0,3)犯了第一个错误。它检查所有数字最多9,然后在那里放9。根据你的算法,它应该是1.挂断是你的check_square方法。你应该

col = (y//3)*3
row = (x//3)*3

修好后,下一个错误会在(x,y) = (1,8)开始,从self.check(1, 8, 1)开始。此广场没有合法的值(使用您的算法到此时为止)(一直到self.check(1, 8, 9))。接下来称为self.check(1, 8, 10)。自val==10起,它返回,然后在self.check(1, 8, 9)的调用中,调用最后一行self.check(x, y-1, val+1),即self.check(1, 7, 10)。它当然会立即返回,因为val == 10。我们回到self.check(1, 8, 8)并调用方法定义的最后一行。执行的旁边是self.check(1, 7, 9),它会生成下一个self.check(1, 8, 1)。看起来熟悉?我们已经在这里,并且在此期间没有状态变化。甚至没有意识到这一点,这将成为一个无限循环。

那令人困惑吗?当然是。程序员试图避免递归是有原因的,除非在教授递归概念时。追踪这些类型的递归错误很困难,但只需几行打印就可以完成。

P.S。你的算法很有趣。我想知道你在哪里找到它......绝对不是人类玩的方式(所有的猜测和编辑)。 FWIW,我首先只会在董事会中插入一个值,如果该值是该正方形的唯一合法移动,并且只有当董事会中的所有空白方块都不明确时才会猜测。

编辑后编辑

将标准库的一部分import copy添加到顶部,然后将__init__更改为self.ogrid = copy.deepcopy(grid)。应该解决你的问题。见https://stackoverflow.com/a/2612815/2100286。您创建网格重复版本的方法可以达到同样的效果。

答案 2 :(得分:0)

我不知道这个片段:

        if y == -1:
            y = 8
            x -= 1

如果y等于行中的最后一个位置,您将其设置为8,这是该行中最后一个位置的索引?这可能是它为什么没有正常进行的原因吗?

答案 3 :(得分:0)

好吧,我解决了我的问题!

这就是我的所作所为。我接受了@Skyking给我的建议,只是通过递归索引而不是我原来的每个索引的值。我改变的第二件事是接受@James Pringles关于如何修复我的check_square函数和复制网格的建议,以便ogridgrid更改时不会改变。 因为我不能给两张绿色支票,所以我把它给了@James Pringle,因为他/她帮助了我最多,但是如果没有@ Skyking的建议我就不可能得到它

这是完成的代码

import sys
import copy

class Board:

    def __init__(self, grid):
        self.grid = grid
        self.ogrid = copy.deepcopy(grid)

    def get_col(self, col):
        column = []
        for i in self.grid:
           column.append(str(i[col]))
        return column

    def get_row(self, row):
        return self.grid[row]

    def check_row(self, r, val):
        row = self.grid[r]

        if str(val) in row:
            return False
        return True

    def check_col(self, column, val):
        col = self.get_col(column)

        if str(val) in col:
            return False
        return True

    def check_square(self, x, y, val):
        col = (y//3)*3
        row = (x//3)*3
        s = ''
        for i in range(row, row+3):
            for j in range(col, col+3):
                s += str(self.grid[i][j])

        if str(val) in s:
            return False
        return True       

    def check_cell(self, x, y, val):
        if self.check_col(y, val) == False:
            return False
        elif self.check_row(x, val) == False:
            return False
        elif self.check_square(x, y, val) == False:
            return False
        return True


    def check(self, x, y):

            if y == 9:
                y = 0
                x += 1

            if x == 9:  
                self.print_board()
                sys.exit()

            if self.ogrid[x][y] == '.':
                for val in range(1,10):
                    if self.check_cell(x, y, val): 
                        self.grid[x][y] = str(val)
                        self.check(x, y+1)

                    if self.ogrid[x][y] == '.':
                        self.grid[x][y] = self.ogrid[x][y]                    
            else:
                self.check(x,y+1)
            return True

    def print_board(self):
        for i in range(0,9):
            for j in range(0,9):
                sys.stdout.write(self.grid[i][j])
                if j == 2 or j == 5:
                    sys.stdout.write(' ')
            if i == 2 or i == 5:
                sys.stdout.write('\n')
            print('')

def main():

    f = open("./easySudoku.txt",'r')
    s = ''

    grid = []
    row = []

    for line in f:
        s += line

    s = s.replace(' ','')
    s = s.replace('\n','')

    for i in range(0,9):
        row = []
        for j in range(0,9):
            row.append(s[(i*9) + j])
        grid.append(row)

    sudoku = Board(grid)
    sudoku.check(0,0)
    print('shouldn\'t be here')

if __name__ == "__main__":
    main()