如何在数独求解器中实现此深度优先搜索

时间:2014-12-21 22:46:40

标签: python depth-first-search sudoku

一些背景故事

我做了一个数独求解器,如果任何给定的单元格只有一个选项来填充它给定的行/列/组,它就可以工作。 但是如果选项高于1,那么这个求解器不起作用,所以我试图制作一个递归求解器,它为每个单元格中的每个可能的选项轮流,我尝试了几种方法来分支选择,但我可以'找到一种方法让它回到状态并采取下一个选项。我的程序似乎总是走同样的道路。

我的代码如下:

  • 首先找到每个单元格的每个可能的移动

  • 然后按最少的选项对它们进行排序。

  • 如果给定单元格中有长度为1的选项,则填充并重新启动,

  • 如果没有,它将遍历选项

理想情况下,它会一直持续到疲惫状态,然后回到最后选择的选项并接受另一个选项。但它没有这样做,它正在重演。

我现在要做的是:

经过一番研究后,我发现我尝试做的事情叫做深度优先搜索,

我找到了this DFS的实现,但是我无法在我的程序中实现它。

这是我的尝试:

def best_moves():
    # get_possible_moves() returns a list of all the available moves for every cell
    # sorting it and taking the first element will get the one
    # that has the least amount options
    return [move for move in sorted(board.get_possible_moves())[0]]

def get_next_nodes(move):
    board.make_this_move(move)
    return best_moves()

def dfs(graph, start):
    visited, stack = set(), [start]
    while stack:
        vertex = stack.pop()
        if vertex not in visited:
            visited.add(vertex)
            nodes = []
            for move in graph[vertex]: 
                nodes.extend(get_next_nodes(move))
            nodes = [node for node in nodes if node not in visited]
            stack.extend(nodes)
    return visited

b = Board(puzzle_hard)
b.create_board()
graph = {'root':best_moves()}
dfs(graph,'root')

它给出了:

Traceback (most recent call last):
  File "C:\Users\user\Documents\python\sudoku\sudoku_solver.py", line 171, in <module>
    dfs(graph,'root')
  File "C:\Users\user\Documents\python\sudoku\sudoku_solver.py", line 162, in dfs
    for move in graph[vertex]:
KeyError: <__main__.PossibleMove instance at 0x0223DB48>

PossibleMove是我为处理给定单元格的可能移动而创建的类。 我可以尝试将其添加到图表中,但为了做到这一点,我需要再次搜索它,我不能这样做,因为我在DFS的中间。

TL;博士

如何预先在图中没有所有节点的情况下实现DSF。

1 个答案:

答案 0 :(得分:0)

几天后,我设法让它发挥作用。

遵循this article中的一些指南(主要是将拼图用作图表节点的字符串)

这个程序的关键要素是游戏状态,董事会在移动之前,我试图使用非常复杂的方法存储游戏状态,但所有这些都需要一些我不知道的知识(然而)。在这种方法中,我现在将游戏状态变为字符串: 例如:

53..7....6..195....98....6.8...6...34..8.3..17...2...6.6....28....419..5....8..79

与:

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

每当我遇到一条存储游戏状态的路径时,请尝试此路径,如果找到另一条路径,我会将其添加到之前的状态。

因此,将来它会将集合中的那些添加为新节点,并找到它们可能的路径。

更简单的例子是:

'123456..'

可能导致:

'123456789'
'123456798'

所以图表表示为:

graph = {'123456..':set(['123456789','123456798'])}

代码是:

def possible_moves(puzzle):
    puzzle = string_to_puzzle((puzzle) # convert the puzzle to a better format(array of 9x9)
    moves = get_next_moves(puzzle) # get the possible moves for the puzzle
    results = set()
    if moves: # if no moves mean, the puzzle is filled or the choices were wrong
        for move in moves:
            this_puzzle = copy(puzzle)
            this_puzzle[move.row][move.column] = move.value
            results.add(puzzle_to_string(this_puzzle)) # convert it back to string and add it to possible moves.
    return results

def dfs(graph, start):
    visited, stack = set(), [start]
    while stack:
        vertex = stack.pop()
        if win(vertex): # check if the current node is the winner
            return string_to_array(vertex) # returns the puzzle compleded in a 9x9 array
        if vertex not in visited:
            visited.add(vertex)
            graph[vertex] = possible_moves(vertex) # finds the next game states for the vertex
            stack.extend(graph[vertex] - visited)
    return "no possible solution"