【问题标题】:Finding all possible walks without repetition in an n*m grid在 n*m 网格中查找所有可能的步行而不重复
【发布时间】:2017-05-16 23:50:35
【问题描述】:

我想找到所有可能的遍历尺寸为n*m 的网格,其中遍历不能多次使用节点,并且允许移动到相邻节点(包括对角线)。

例如,1*1 网格是微不足道的;只有 1 次可能的步行,即[(0,0)]1*2 网格有 4 种可能的游走,即 [(0,0)][(0,1)][(0,0),(0,1)][(0,1),(0,0)]2*2 网格有 4 个长度为 1 的小步,分别是 [(0,0)][(0,1)][(1,0)][(1,1)],12 个长度为 2 的步 [(0,0), (0,1)][(0,0),(1,0)][(0,0),(1,1)] 等等,长度为 3 的 16 次步行([(0,0),(0,1),(1,1)] 等)和长度为 4 的 16 次步行([(0,0),(0,1),(1,1),(1,0)] 等)。

这是我在 python 中实现递归算法以列出 n*m 网格中所有可能的行走的尝试,但它没有返回正确的解决方案。我通过遍历所有起点来接近它,然后形成一个可能的后续步骤的递归树。

dim=(2,2)

walks=[]

def main():
    for row in range(dim[0]):
        for col in range(dim[1]):
            walked=[[0 for i in range(dim[0])] for j in range(dim[1])]
            walkTree(row, col, walked, [])
    print(walks)

def walkTree(row, col, walked, leading):
    walks.append( leading+[(row,col)] )
    walked[row][col]=1
    leading.append((row,col))

    if row-1 >= 0:
        if col-1 >= 0:
            if not walked[row-1][col-1]:
                walkTree(row-1, col-1, walked, leading)
        if not walked[row-1][col]:
                walkTree(row-1, col, walked, leading)
        if col+1 < dim[1]:
            if not walked[row-1][col+1]:
                walkTree(row-1, col+1, walked, leading)

    if col-1 >= 0:
        if not walked[row][col-1]:
            walkTree(row, col-1, walked, leading)
    if col+1 < dim[1]:
        if not walked[row][col+1]:
            walkTree(row, col+1, walked, leading)

    if row+1 < dim[0]:
        if col-1 >= 0:
            if not walked[row+1][col-1]:
                walkTree(row+1, col-1, walked, leading)
        if not walked[row+1][col]:
                walkTree(row+1, col, walked, leading)
        if col+1 < dim[1]:
            if not walked[row+1][col+1]:
                walkTree(row+1, col+1, walked, leading)


if __name__=='__main__':
    main()

非常感谢任何关于我的代码为什么不起作用的帮助或解决此问题的更好方法。谢谢!

【问题讨论】:

  • 当您说您也对“更好的方式”感兴趣时,是否包括外部包?
  • 当然,只要它可以迭代所有可能的遍历。
  • 您没有向我们显示错误的输出,也没有提供任何执行跟踪。你对debug这个做了什么,你从中学到了什么?
  • 我在 (2,1) 和 (1,2) 网格上尝试了您的代码。在这两种情况下,它都会出现索引超出范围的故障。
  • 如果这是我的问题,我会使用OEIS 并搜索self-avoiding walk。我会为您执行此操作,但至少需要序列的前四个数字。

标签: python algorithm


【解决方案1】:

还有另一种方法。您可以将您的问题简化为图形上的已知问题,然后使用图形库来解决它。

  1. 将单元定义为节点,用边连接所有相邻单元(包括对角线)。

  2. 添加两个额外的节点 s 和 d。将 s 连接到所有初始节点,对 d 执行相同操作。

  3. 那么你的问题就变成了:找到 s 和 d 之间的所有简单路径。 python 的networkx 库可以做到(见https://networkx.readthedocs.io/en/stable/reference/generated/networkx.algorithms.simple_paths.all_simple_paths.html),R 的igraph 也可以(它会更快)。由于某些奇怪的原因,python 的 igraph 中似乎没有类似的功能。

【讨论】:

  • 作为一名即将毕业并上过图论课程的数学本科生,我有点惭愧我自己没有看到这种图论方法;特别是考虑到我已经看到类似的技术用于稳定婚姻问题等。这比我的方法要优雅得多,imo - 非常感谢您的帮助!
  • 作为更新:我已经成功实施了这个解决方案,它按预期工作;虽然我发现它对于我的 4x4 网格应用程序来说效率太低了(如果你好奇的话,一个“Boggle 求解器”),因为 4x4 Kings 图有 1200 万条简单路径(所以算法被“卡住”了无望的路径不包含任何文字)。然而,这种方法为我如何更有效地解决(有点启发式而不是详尽地)我的问题带来了更好的想法。即首先搜索哈密顿路径(因为更长的单词更好),并删除了各种顶点。再次感谢!
  • @user406579,不客气;很高兴听到你玩得开心:)
【解决方案2】:

我添加了一些跟踪工具。从 (0,0) 开始的输出低于此值。 print语句是按级别缩进的,我只是在几个有用的地方打印出当前位置。

代码:

dim=(2,2)
walks=[]
indent = ""

def main():
    for row in range(dim[0]):
        for col in range(dim[1]):
            walked=[[False for i in range(dim[0])] for j in range(dim[1])]
            walkTree(row, col, walked, [])

    for dist in range(1, dim[0]*dim[1]+1):
        print "length", dist, "\n\t", [path for path in walks if len(path) == dist]

def walkTree(row, col, walked, leading):
    global indent
    print indent, "ENTER", "pos", row, col, "walked=", walked, "lead=", leading
    indent += "  "
    walks.append( leading+[(row,col)] )
    walked[row][col] = True
    leading.append((row,col))

    print indent, "ROW-", row, col, walked
    if row-1 >= 0:
        if col-1 >= 0:
            if not walked[row-1][col-1]:
                walkTree(row-1, col-1, walked, leading)
        if not walked[row-1][col]:
                walkTree(row-1, col, walked, leading)
        if col+1 < dim[1]:
            if not walked[row-1][col+1]:
                walkTree(row-1, col+1, walked, leading)

    print indent, "COL-", row, col, walked
    if col-1 >= 0:
        if not walked[row][col-1]:
            walkTree(row, col-1, walked, leading)

    print indent, "COL+", row, col, walked
    if col+1 < dim[1]:
        if not walked[row][col+1]:
            walkTree(row, col+1, walked, leading)

    print indent, "ROW+", row, col, walked
    if row+1 < dim[0]:
        if col-1 >= 0:
            if not walked[row+1][col-1]:
                walkTree(row+1, col-1, walked, leading)
        if not walked[row+1][col]:
                walkTree(row+1, col, walked, leading)
        if col+1 < dim[1]:
            if not walked[row+1][col+1]:
                walkTree(row+1, col+1, walked, leading)

    indent = indent[2:]

在输出中(我只打印了前 1/4),请注意您是如何从 (0,0) 起点仅覆盖一条线性路径的:您先到 (0,1),然后是 (1,0) ,最后是 (1,1)。但是,当您从每个步骤返回时,您无法涵盖任何其他选项,例如移动 (0,0) => (0,1) => (1,1) 并从那里移动到 (1,0) .

致命的缺陷是你在回溯时没有管理你的状态列表。在下面显示的情况下,即使您回溯到 (0,0),您仍然会将整个网格标记为 walked。相反,尝试任一重置代码:就在您从例程返回之前,

walked[row,col] = False

...或制作walked 的本地副本,而不是处理本质上是全局主列表的内容。

既然你有提示,我会把leading 的处理留给你。

输出:

 ENTER pos 0 0 walked= [[False, False], [False, False]] lead= []
   ROW- 0 0 [[True, False], [False, False]]
   COL- 0 0 [[True, False], [False, False]]
   COL+ 0 0 [[True, False], [False, False]]
   ENTER pos 0 1 walked= [[True, False], [False, False]] lead= [(0, 0)]
     ROW- 0 1 [[True, True], [False, False]]
     COL- 0 1 [[True, True], [False, False]]
     COL+ 0 1 [[True, True], [False, False]]
     ROW+ 0 1 [[True, True], [False, False]]
     ENTER pos 1 0 walked= [[True, True], [False, False]] lead= [(0, 0), (0, 1)]
       ROW- 1 0 [[True, True], [True, False]]
       COL- 1 0 [[True, True], [True, False]]
       COL+ 1 0 [[True, True], [True, False]]
       ENTER pos 1 1 walked= [[True, True], [True, False]] lead= [(0, 0), (0, 1), (1, 0)]
         ROW- 1 1 [[True, True], [True, True]]
         COL- 1 1 [[True, True], [True, True]]
         COL+ 1 1 [[True, True], [True, True]]
         ROW+ 1 1 [[True, True], [True, True]]
       ROW+ 1 0 [[True, True], [True, True]]
   ROW+ 0 0 [[True, True], [True, True]]
 ENTER pos 0 1 walked= [[False, False], [False, False]] lead= []
   ROW- 0 1 [[False, True], [False, False]]
   COL- 0 1 [[False, True], [False, False]]
 ...

【讨论】:

  • 谢谢,我可以看到问题所在,但我不知道是什么原因造成的。我已经尝试按照您的建议创建 walk 的本地副本,但似乎没有什么不同。此外,我不明白为什么该函数的其他迭代会通过前一次迭代的 walk - 我的意思是让每个迭代都使用自己的 walk 列表运行,该列表在调用时通过。我不明白为什么创建本地副本不能解决此问题。
  • 您发布的代码没有创建walked 的本地副本:您在主程序中创建了一个并将其传递给每个迭代。您是否通过一些打印语句检查了您的功能?
猜你喜欢
  • 1970-01-01
  • 2016-04-23
  • 2021-10-26
  • 2016-05-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-29
  • 2018-09-02
相关资源
最近更新 更多