"哈密顿"使用Python的路径

时间:2017-12-26 18:56:25

标签: python recursion backtracking recursive-backtracking

我正在尝试使用Python实现遍历所有图形顶点的任意路径(不一定是循环)的递归搜索。这是我的代码:

def hamilton(G, size, pt, path=[]):
    if pt not in set(path):
        path.append(pt)
        if len(path)==size:
            return path
        for pt_next in G[pt]:
            res_path = [i for i in path]
            hamilton (G, size, pt_next, res_path)

此处,pt是起始点,path是所有以前遍历的顶点列表,不包括pt,默认为空。问题是,每当找到这样的路径时,return语句引用过程的一些内部调用,因此程序不会终止或返回路径。

例如,取G = {1:[2,3,4], 2:[1,3,4], 3:[1,2,4], 4:[1,2,3]}(即完整的4图)并运行hamilton(G,4,1,[])。它返回None,但如果您打印路径而不是将其作为值返回,您将看到它实际上找到从1开始的所有六条路径。

如果我告诉程序将路径与return语句一起打印,它最终会打印所有这些路径,因此运行的时间比需要的时间长得多。

如何修复代码,以便在找到第一个合适的路径后终止执行?

2 个答案:

答案 0 :(得分:3)

这是不使用递归DFS从特定顶点start_v搜索汉密尔顿路径的解决方案。

它是为数组的图形表示散列图制作的:

G = {0:[1], 1:[0, 2], 2:[1, 3], 3:[2, 4, 5], 4:[3, 6], 5:[3], 6:[4]}
def hamilton(graph, start_v):
  size = len(graph)
  # if None we are -unvisiting- comming back and pop v
  to_visit = [None, start_v]
  path = []
  while(to_visit):
    v = to_visit.pop()
    if v : 
      path.append(v)
      if len(path) == size:
        break
      for x in set(graph[v])-set(path):
        to_visit.append(None) # out
        to_visit.append(x) # in
    else: # if None we are comming back and pop v
      path.pop()
  return path

如果通过集合的哈希图表示图形,则可以加快速度:

G = {0:{1}, 1:{0, 2}, 2:{1, 3}, 3:{2, 4, 5}, 4:{3, 6}, 5:{3}, 6:{4}}

并维护访问集,并且不使用set(path)

然后解决方案会稍微快一点:

def hamilton(graph, start_v):
  size = len(graph)
  # if None we are -unvisiting- comming back and pop v
  to_visit = [None, start_v]
  path = []
  visited = set([])
  while(to_visit):
    v = to_visit.pop()
    if v : 
      path.append(v)
      if len(path) == size:
        break
      visited.add(v)
      for x in graph[v]-visited:
        to_visit.append(None) # out
        to_visit.append(x) # in
    else: # if None we are comming back and pop v
      visited.remove(path.pop())
  return path

答案 1 :(得分:2)

基本错误是递归调用的结果需要返回,如果它没有导致死胡同。

此外,如果点G[pt]没有邻居,IndexError会提升pt。使用dict.get可以很容易地解决这个问题。

def hamilton(G, size, pt, path=[]):
    print('hamilton called with pt={}, path={}'.format(pt, path))
    if pt not in set(path):
        path.append(pt)
        if len(path)==size:
            return path
        for pt_next in G.get(pt, []):
            res_path = [i for i in path]
            candidate = hamilton(G, size, pt_next, res_path)
            if candidate is not None:  # skip loop or dead end
                return candidate
        print('path {} is a dead end'.format(path))
    else:
        print('pt {} already in path {}'.format(pt, path))
    # loop or dead end, None is implicitly returned

实施例

>>> G = {1:[2,3,4], 2:[1,3,4], 3:[1,2,4], 4:[1,2,3]}
>>> hamilton(G, 4, 1)
hamilton called with pt=1, path=[]
hamilton called with pt=2, path=[1]
hamilton called with pt=1, path=[1, 2]
pt 1 already in path [1, 2]
hamilton called with pt=3, path=[1, 2]
hamilton called with pt=1, path=[1, 2, 3]
pt 1 already in path [1, 2, 3]
hamilton called with pt=2, path=[1, 2, 3]
pt 2 already in path [1, 2, 3]
hamilton called with pt=4, path=[1, 2, 3]
[1, 2, 3, 4]
>>> G = {1:[2], 2:[3,4], 4:[3]}
>>> hamilton(G, 4, 1)
hamilton called with pt=1, path=[]
hamilton called with pt=2, path=[1]
hamilton called with pt=3, path=[1, 2]
path [1, 2, 3] is a dead end
hamilton called with pt=4, path=[1, 2]
hamilton called with pt=3, path=[1, 2, 4]
[1, 2, 4, 3]
>>> G = {1:[2], 2:[3,4], 4:[3]}
>>> hamilton(G, 4, 2)
hamilton called with pt=2, path=[]
hamilton called with pt=3, path=[2]
path [2, 3] is a dead end
hamilton called with pt=4, path=[2]
hamilton called with pt=3, path=[2, 4]
path [2, 4, 3] is a dead end
path [2, 4] is a dead end
path [2] is a dead end
None

请注意,由于“可变默认参数”问题,多次使用该函数会导致错误的结果。但这不是这个答案的范围。