【问题标题】:Find all combinations in a 3x3 matrix following some rules按照一些规则查找 3x3 矩阵中的所有组合
【发布时间】:2018-08-18 06:24:23
【问题描述】:

给定一个 3x3 矩阵:

|1 2 3|  
|4 5 6|  
|7 8 9|  

我想通过按照以下规则连接此矩阵中的数字来计算所有组合:

  • 组合宽度在 3 到 9 之间
  • 一个号码只能使用一次
  • 只能连接相邻的数字

一些示例:123、258、2589、123654 等。
例如 1238 不是一个好的组合,因为 3 和 8 不相邻。 123和321的组合不一样。
我希望我的描述清楚。
如果有人有任何想法,请告诉我。其实我不知道如何开始:D。谢谢

【问题讨论】:

  • 首先,我会查找递归,也许是广度或深度优先搜索,同时跟踪“已访问”节点。
  • 如果你可以使用例如clojure、scheme 或 prolog 然后它们内置了基于约束的自动求解器。例如您会在 clojure 中找到大量数独求解器示例。

标签: algorithm matrix


【解决方案1】:

这是一个搜索问题。您可以使用简单的深度优先搜索和递归编程来快速解决问题。类似于以下内容:

func search(matrix[N][M], x, y, digitsUsed[10], combination[L]) {
    if length(combination) between 3 and 9 {
       add this combination into your solution
    }

    // four adjacent directions to be attempted
    dx = {1,0,0,-1}
    dy = {0,1,-1,0}
    for i = 0; i < 4; i++ {
       next_x = x + dx[i]
       next_y = y + dy[i]
       if in_matrix(next_x, next_y) and not digitsUsed[matrix[next_x][next_y]] {
           digitsUsed[matrix[next_x][next_y]] = true
           combination += matrix[next_x][next_y]
           search(matrix, next_x, next_y, digitsUsed, combination)

           // At this time, sub-search starts with (next_x, next_y) has been completed.
           digitsUsed[matrix[next_x][next_y]] = false
       }
   }
}

因此您可以对矩阵中的每个网格运行搜索功能,并且您的解决方案中的每个组合都彼此不同,因为它们从不同的网格开始。

另外,我们不需要记录表明矩阵中的一个网格是否被遍历的状态,因为每个数字只能使用一次,因此已经遍历的网格将永远不会被再次遍历,因为它们数字已包含在组合中。

【讨论】:

    【解决方案2】:

    以下是 Python 3 中作为递归深度优先探索的可能实现:

    def find_combinations(data, min_length, max_length):
        # Matrix of booleans indicating what values have been used
        visited = [[False for _ in row] for row in data]
        # Current combination
        comb = []
        # Start recursive algorithm at every possible position
        for i in range(len(data)):
            for j in range(len(data[i])):
                # Add initial combination element and mark as visited
                comb.append(data[i][j])
                visited[i][j] = True
                # Start recursive algorithm
                yield from find_combinations_rec(data, min_length, max_length, visited, comb, i, j)
                # After all combinations with current element have been produced remove it
                visited[i][j] = False
                comb.pop()
    
    def find_combinations_rec(data, min_length, max_length, visited, comb, i, j):
        # Yield the current combination if it has the right size
        if min_length <= len(comb) <= max_length:
            yield comb.copy()
        # Stop the recursion after reaching maximum length
        if len(comb) >= max_length:
            return
        # For each neighbor of the last added element
        for i2, j2 in ((i - 1, j), (i, j - 1), (i, j + 1), (i + 1, j)):
            # Check the neighbor is valid and not visited
            if i2 < 0 or i2 >= len(data) or j2 < 0 or j2 >= len(data[i2]) or visited[i2][j2]:
                continue
            # Add neighbor and mark as visited
            comb.append(data[i2][j2])
            visited[i2][j2] = True
            # Produce combinations for current starting sequence
            yield from find_combinations_rec(data, min_length, max_length, visited, comb, i2, j2)
            # Remove last added combination element
            visited[i2][j2] = False
            comb.pop()
    
    # Try it
    data = [[1, 2, 3],
            [4, 5, 6],
            [7, 8, 9]]
    min_length = 3
    max_length = 9
    for comb in find_combinations(data, min_length, max_length):
        print(c)
    

    输出:

    [1, 2, 3]
    [1, 2, 3, 6]
    [1, 2, 3, 6, 5]
    [1, 2, 3, 6, 5, 4]
    [1, 2, 3, 6, 5, 4, 7]
    [1, 2, 3, 6, 5, 4, 7, 8]
    [1, 2, 3, 6, 5, 4, 7, 8, 9]
    [1, 2, 3, 6, 5, 8]
    [1, 2, 3, 6, 5, 8, 7]
    [1, 2, 3, 6, 5, 8, 7, 4]
    [1, 2, 3, 6, 5, 8, 9]
    [1, 2, 3, 6, 9]
    [1, 2, 3, 6, 9, 8]
    [1, 2, 3, 6, 9, 8, 5]
    [1, 2, 3, 6, 9, 8, 5, 4]
    [1, 2, 3, 6, 9, 8, 5, 4, 7]
    ...
    

    【讨论】:

      【解决方案3】:

      查看所有组合并取连接的组合:

      import itertools
      
      def coords(n):
          """Coordinates of number n in the matrix."""
          return (n - 1) // 3, (n - 1) % 3
      
      def adjacent(a, b):
          """Check if a and b are adjacent in the matrix."""
          ai, aj = coords(a)
          bi, bj = coords(b)
          return abs(ai - bi) + abs(aj - bj) == 1
      
      def connected(comb):
          """Check if combination is connected."""
          return all(adjacent(a, b) for a, b in zip(comb, comb[1:]))
      
      for width in range(3, 10):
          for comb in itertools.permutations(range(1, 10), width):
              if connected(comb):
                  print(comb)
      

      【讨论】:

      • 谢谢@gus!这对我来说似乎很好,但我想用 Swift 编写这个算法(我在描述中没有提到,我的错)。我的主要问题是我根本没有得到这return all(adjacent(a, b) for a, b in zip(comb, comb[1:])) 行代码。我不知道 Python,这行太令人困惑了。我理解这个概念,但无法弄清楚这条线是如何工作的,以及如何将它“翻译”成 Swift。
      • 另一种说法是all(adjacent(comb[i], comb[i+1]) for i in range(len(comb)-1)))。如果comb 中相邻的每两个数字在矩阵中也相邻,则返回 true。我不知道 Swift,但这句话可能不是最难翻译的。这里的棘手部分隐藏在 itertools.permutations 调用中,如果您在 Swift 中没有类似的东西,您将不得不自己实现它或使用不同的方法。
      猜你喜欢
      • 2011-11-30
      • 2011-12-01
      • 2016-03-11
      • 1970-01-01
      • 2012-11-25
      • 1970-01-01
      • 2014-01-14
      • 2019-09-01
      相关资源
      最近更新 更多