【问题标题】:How to improve Knight's tour with Warnsdorff's rule?如何利用 Warnsdorff 的规则改进 Knight 的巡回赛?
【发布时间】:2011-12-06 15:44:07
【问题描述】:

我知道有几个类似的线程,但即使在 SO 之外我也没有找到解决方案。 这是我的问题: 我为骑士之旅问题实现了 Warnsdorff 的算法http://en.wikipedia.org/wiki/Knight%27s_tour,但在某些情况下它并没有给出解决方案。在我读到的某些地方,它可以通过一些更改更好地工作,但没有人指定哪些更改是那些。有人知道解决方案吗?我知道其他算法,但它们要复杂得多。

即使对于 8x8 棋盘,它有时也不能提供好的解决方案。我认为阅读我的代码没有意义,因为它是经典的 Warnsdorff 的:检查可能的移动,并在下一步中选择移动最少的一个。

【问题讨论】:

  • 你到底在问什么?如何改进 Warnsdorff 规则?维基百科指出,Warnsdorff 的规则应该为任何起始方格提供解决方案。此外,Warnsdorff 规则也没有太大的改进空间。你具体在想什么?
  • @CaptainGiraffe 它并不完美,真的。我刚刚发现了这个:mirran.web.surftown.se/knight/bWarnsd.htm 并试图看看它是否是一个真正的改进。这部分,当你有超过 1 个可能的移动时,具有相同的质量。这个改进说,选择离棋盘中心最远的那个。当我实施它时会看到。
  • 我明白了。 wiki 文章的表述非常具有误导性。是的,我熟悉抢七局中的“最远方位”。我仍然不确定你在问什么。
  • 我要求对此算法进行改进,以便在比经典算法更多的情况下提供更好的解决方案。人们已经尝试过并且效果更好的东西。就像我在之前的编辑中提供的链接一样。是的,维基有误导性。如果我使用上面链接中的解决方案解决了问题,我会要求管理员关闭此问题。
  • github.com/douglassquirrel/warnsdorff/blob/master/… 中提出了一种改进的 Warnsdorff 算法,声称它非常适合 > 112 的棋盘

标签: algorithm graph graph-theory chess knights-tour


【解决方案1】:

W+ 的链接不再存在,导致接受的答案不可用。

Warnsdorff 算法的改进包括:

Arnd Roth 的主张: Roth 认为 Warnsdorff 启发式的问题在于随机抢七规则。 提议是通过选择距棋盘中心欧几里德距离最大的继任者来打破平局。

这里的问题是当多个后继者共享相同的距离时会发生什么。
Arnd Roth 声称这种改进首先 在一个有 428 行的板上失败并且所有的失败率都低于 1% 少于 2000 行的板。

艾拉·波尔的提议: 为了打破这种关系,波尔决定再次应用 Warnsdorff 的规则。为了实现这一点,我们必须取所有未访问邻居的度数之和,以及并列的后继者的度数之和,并选择总和最小的平方。

这里的一个问题是,无论我们应用 Warnsdorff 规则多少次,我们都会导致平局(两个相邻的 广场)如果我们从角落广场开始。此外,如果我们申请 Warnsdorff 的启发式多次渐近 这等于穷举搜索。

Pohl 还建议,如果我们在第二次应用 Warnsdorff 后未能产生继任者,则通过使用固定的任意正方形顺序来打破平局。他声称这成功地在 8*8 板上产生了解决方案。

通过使用这些增强功能之一,我们更有可能通过防止可能的锁定来系统地创建解决方案。

【讨论】:

    【解决方案2】:

    这是我发现的:

    请注意,这仍然不是一个确定的答案,我也不是图论专家,所以这些只是观察结果。

    我将把经典的 Warnsdorff 启发式称为“W”。 对http://mirran.web.surftown.se/knight/bWarnsd.htm(缓存:http://web.archive.org/web/20120213164632/http://mirran.web.surftown.se/knight/bWarnsd.htm)的改进将被称为“W+”。 https://github.com/douglassquirrel/warnsdorff/blob/master/5_Squirrel96.pdf?raw=true 的改进将是“W2”。 水平字段数为“x”,垂直字段数为“y”。

    所以,这是我的观察结果。

    简短版:

    W 很简单,但在很多情况下它无法提供解决方案。这首先引发了这个问题。 W+ 也很简单,并且有很大的改进,尤其是在大型电路板上。 W2 实现起来要复杂得多,与 W+ 相比,它似乎并没有给出更好的结果。所以我投票给W+。无论如何,这就是我将使用的变体。

    长版:

    W

    优点: 与其他 Knights Tour 算法相比,简单。 与W+相比,它并没有真正的优势。 与 W2 相比,它更容易实现。

    缺点: 有很多解决方案的情况,但W无法提供 它往往会与更大的电路板 (50+) 相混淆

    W+

    优点: 与其他 Knights Tour 算法相比,简单。 与 W 相比:它可以在更多情况下提供解决方案,并且几乎不比 W 复杂。 与 W2 相比,它更容易实现,而且 W+ 也适用于非方形电路板。例如 10x20。

    缺点: 与W相比,它没有缺点。 与其他骑士游览算法相比,这个算法在某些情况下可能会卡住。 W+ 最困难的是小板,如 5x5、6x6、7x7、9x9 等。正如 Wiki 中所述,当 x 和 y 都是偶数时,它会出现问题。另一方面,当 x 和 y 是偶数,但大于 9 时,W+ 似乎设法找到了解决方案。 与W2相比,我没有遇到任何劣势。

    W2

    优点: 与 W 相比,它在更多情况下提供了解决方案,尤其是对于大型电路板。 与 W+ 相比,我没有注意到优势。

    缺点: 与 W 和 W+ 相比的实现。

    结论:

    我认为 W+ 实际上是最容易接受的。不要忘记它并不完美。我不得不说,我的实现不允许真正的大板。我测试了高达 90x90(8100 个节点)的 W+,它仍然提供了解决方案。虽然,由于时间有限,我没有进行广泛的测试。我希望这对以前遇到过这个问题的人有所帮助。 因为这不是一个确定的答案,所以我暂时不会接受它,希望有人出现可以给出完整的答案。 抱歉读了这么久。

    【讨论】:

      【解决方案3】:

      这是 Python 2 中的一个工作示例,它使用计时器来遍历板并说明解决方案。我找到了最接近板边缘的节点以用于决胜局,如果两者相同,我只返回第一个。这似乎是一个自然的选择,因为如果棋盘是空的,靠近边缘的单元格应该有更少的可能移动。似乎运作良好,但正如 Panos 提到的,Arnd Roth 的提议有 1% 的失败率。可以轻松修改此代码以使用 Ira Pohl 的命题。可以修改访问节点以针对都具有最小移动的节点再次运行 possible_unvisited_moves。在平局的情况下,使用第一个应该可以工作。

      class GNode(object):
          """ Used to represent a node in the graph """
          def __init__(self, value, row=None, col=None):
              self.row = row  # allows node to know its loc. in array
              self.col = col
              self.value = value
      
      def setup_graph(n):
          """ Create x by x grid (graph inside an array). """
          graph = []
          for row in range(n):
              graph.append([])
              for col in range(n):
                  graph[row].append(GNode(None,row=row, col=col))
          return graph
      
      def possible_moves(graph_node, total):
          """ Find out all possible moves for the current node position. """
          moves = []
          move_patterns = [[-1,-2], [-1, 2], [-2, 1], [-2, -1], [1, -2], [1, 2], [2, 1], [2, -1]]
          for row, col in move_patterns:
              move_row = graph_node.row + row; move_col = graph_node.col + col
              if move_row >= 0 and move_col >= 0 and move_row < total and move_col < total:
                  moves.append([move_row, move_col])
          return moves
      
      def possible_unvisited_moves(graph_node, grid):
          """Get all possible moves and weed out the ones that are already visited. 
             visited means they have a value.
          """
          moves = possible_moves(graph_node, len(grid))
          unvisited = []
          for move in moves:
              if grid[move[0]][move[1]].value is None:
                  unvisited.append(move)
          return unvisited
      
      
      def closest_to_edge(pos1, pos2, total):  
          """ Find out which position is closest to the edge of board, and return it. 
              pos are 2 arrays [x,y], total is the board size (length)
          """
          total -= 1
          pos1_min = min(total - pos1[0], pos1[0], pos1[1], total-pos1[1])
          pos2_min = min(total - pos2[0], pos2[0], pos2[1], total-pos2[1])
          if pos2_min < pos1_min: return pos2
          return pos1  # default
      
      
      def visit_node(graph_node, graph):
          """ Check possible unvisited moves from the pos & find next move to make """
          graph_node.value = "{}{}".format(graph_node.row, graph_node.col)  # visited
          p_moves = possible_unvisited_moves(graph_node, graph)
          next_move = None
          min_no_moves = 9 # highest can be 8 for knights move pattern
          for move in p_moves:
              next_moves = possible_unvisited_moves(graph[move[0]][move[1]], graph)
              if len(next_moves) < min_no_moves:
                  next_move = move
                  min_no_moves = len(next_moves)
              elif len(next_moves) == min_no_moves:
                  min_move = closest_to_edge(next_move, move, len(graph))
                  if min_move != next_move:
                      next_move = min_move
                      min_no_moves = len(next_moves)
          if next_move:
              os.system(clear_screen) 
              print "This Move: [",graph_node.row, ",",  graph_node.col, "]. Next Move: ", next_move, "Min Moves from next: ", min_no_moves
              display_graph(graph)
              import time
              time.sleep(.5)
              return visit_node(graph[next_move[0]][next_move[1]], graph)
          else:
              os.system(clear_screen) 
              display_graph(graph)
              return "Done"
      
      def display_graph(graph):
          print ""
          for row in graph:
              row_vals = []
              for cell in row:
                  row_vals.append(cell.value)
              print row_vals
      
      import os
      clear_screen = 'cls' if os.name == 'nt' else 'clear'
      
      graph = setup_graph(8)
      
      print visit_node(graph[4][4], graph)
      

      玩得开心。 :)

      【讨论】:

        【解决方案4】:

        我认为在应用 Warnsdorff 规则时最容易被忽视的是正在构建的路径有两端。当将两级平局规则应用于路径的两端时,可以获得相当好的结果。而且,作为额外的奖励,产生的重入旅游的数量大大增加。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-11-05
          • 1970-01-01
          • 2019-02-04
          相关资源
          最近更新 更多