在马踏棋盘问题的深度优先搜索中,我缺少什么?

4

我正在尝试使用DFS解决骑士巡逻问题。我生成了我的图形(在这个例子中,我有一个5x5矩阵):

{
  0: set([11, 7]),
  1: set([8, 10, 12]),
  2: set([9, 11, 5, 13]),
  3: set([12, 14, 6]),
  4: set([13, 7]),
  5: set([16, 2, 12]), 6: set([17, 3, 13, 15]), 7: set([0, 4, 10, 14, 16, 18]), 8: set([19, 1, 11, 17]), 9: set([2, 12, 18]), 10: set([1, 17, 21, 7]), 11: set([0, 2, 8, 18, 20, 22]), 12: set([1, 3, 5, 9, 15, 19, 21, 23]), 13: set([2, 4, 6, 16, 22, 24]), 14: set([23, 17, 3, 7]), 15: set([12, 22, 6]), 16: set([23, 7, 5, 13]), 17: set([6, 8, 10, 14, 20, 24]), 18: set([9, 11, 21, 7]), 19: set([8, 12, 22]), 20: set([17, 11]), 21: set([10, 12, 18]), 
  22: set([19, 11, 13, 15]),
  23: set([16, 12, 14]),
  24: set([17, 13])
}

然后我尝试调用DFS来找到长度为25的路径(每个方块都被到达)。为了做到这一点,我跟踪当前路径,将其与所需长度进行比较,如果没有递归地从所有邻居中展开DFS,则将其删除。如果没有未选中的邻居(我们到达了死胡同,但仍然有应该到达的方块),我会从路径中删除最后一个元素。

def knightTour(current, limit, path):
    if len(path) == limit:
        return path

    path.append(current)

    neighbors = graph[current]
    if len(neighbors):
        for i in neighbors:
            if i not in set(path):
                return knightTour(i, limit, path)
    else:
        path.pop()
        return False

knightTour(0, 24, [])

我可能错过了一些明显的东西,因为在我的情况下,它找不到完整路径,卡在了[0,11,2,9,12,1,8,19,22,13,4,7,10,17,6,3,14,23,16]。你知道我的错误在哪里吗?


3
你还没有实现回溯 - 你的函数在第一个未被访问的邻居处就return了,而不是尝试所有路径。 - jonrsharpe
1
此外,在将当前节点添加到路径后,应该进行成功条件检查。 - tobias_k
2个回答

4

如果您没有回溯,那么您的算法最终会陷入一条无法工作的路径(除非它有幸第一次就得到了结果)。以下是一个稍微简单一些的实现,可以正常工作:

def knights_tour(graph, path=None):
    if path is None:
        path = [min(graph)]
    if len(path) == len(graph):
        return path
    visited = set(path)
    for neighbour in graph[path[-1]]:
        if neighbour not in visited:
            path.append(neighbour)
            if knights_tour(graph, path):
                return path
            path.pop()

请注意,只有在递归调用返回 true 时(即已找到完整路径),此方法才返回 path,否则它会移除该邻居并继续检查其他邻居的可能路径。

3

除了Jon的优秀答案之外,这里提供另一个版本更接近你的原始代码,这样你就能看到问题所在:

def knightTour(current, limit, path):
    path.append(current)    # add current before returning, or the last 
    if len(path) == limit:  # node will be missing in the returned path
        return path
                            # (no need to check length)
    for i in graph[current]:
        if i not in path:   # (no point in creating a set in each iteration)
            tour = knightTour(i, limit, path)
            if tour:        # only return the path if it is not None, i.e.
                return tour # if the recusion was succesful (backtracking)
    else:
        path.pop()          # (use implicit return None)

调用knightTour(0, 25, []),结果是[0, 11, 2, 9, 12, 1, 8, 19, 22, 13, 4, 7, 10, 21, 18, 17, 6, 3, 14, 23, 16, 5, 15, 20, 24]


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接