我在骑士之旅的 DFS 中缺少什么?
What am I missing in DFS in knight tour?
我正在尝试使用 DFS 解决 knight tour problem。我生成了我的图表(在这个例子中我有 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]
。知道我的错误在哪里吗?
你没有回溯,所以你的算法最终陷入了一条无法工作的路径(除非它运气好并第一次得到结果)。这是一个稍微简单的有效实现:
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()
请注意,如果递归调用已返回 truth-y(即已找到完整路径),这仅 returns path
,否则它会删除该邻居并继续检查可能的来自其他邻居的路径。
作为 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]
我正在尝试使用 DFS 解决 knight tour problem。我生成了我的图表(在这个例子中我有 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]
。知道我的错误在哪里吗?
你没有回溯,所以你的算法最终陷入了一条无法工作的路径(除非它运气好并第一次得到结果)。这是一个稍微简单的有效实现:
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()
请注意,如果递归调用已返回 truth-y(即已找到完整路径),这仅 returns path
,否则它会删除该邻居并继续检查可能的来自其他邻居的路径。
作为 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]