【问题标题】:Pythonic and efficient way of finding adjacent cells in grid在网格中查找相邻单元格的 Pythonic 和有效方法
【发布时间】:2010-03-03 17:05:28
【问题描述】:

我正在使用 pyglet/openGL 在 Python 中构建一个基于磁贴的应用程序,其中我需要找到给定单元格的所有相邻单元格。我在笛卡尔网格的一个象限中工作。每个单元格都有一个 x 和 y 值,表示它在网格中的位置( x_coord 和 y_coord )。这些不是像素值,而是网格位置。我正在寻找一种有效的方法来获取相邻的单元格。最多有 8 个可能的相邻单元格,但由于网格的边界,可能只有 3 个。一个简单但可能效率低下的方法的伪代码如下所示:

def get_adjacent_cells( self, cell ):
     result = []
     x_coord = cell.x_coord
     y_coord = cell.y_coord
     for c in grid.cells:
          if c.x_coord == x_coord and c.y_coord == y_coord: # right
               result.append( c )
          if c.x_coord == x_coord - 1 and c.y_coord == y_coord + 1: # lower right
               result.append( c )
          if c.x_coord == x_coord - 1 and c.y_coord == y_coord: # below
               result.append( c )
          if c.x_coord == x_coord - 1 and c.y_coord == y_coord - 1: lower left
               result.append( c )
          if c.x_coord == x_coord and c.y_coord == y_coord - 1: right
               result.append( c )
          // -- similar conditional for remaining cells

这可能工作得很好,尽管此代码可能需要运行每一帧并且在更大的网格中它可能会影响性能。关于更精简和更少 CPU 密集型方法的任何想法?或者,我应该采用这种方法吗?

提前致谢。

【问题讨论】:

  • 如果你想保持你这样做的方式,那么我至少会做一个计数器来计算有多少结果被放入了结果中。当它达到 8 时,跳出循环。另外,仅在追加单元格时检查它是否等于 8。

标签: python grid pyglet


【解决方案1】:

我不清楚单元格中是否除了 x 和 y 坐标之外还有其他信息。无论如何,我认为需要改变数据结构以使其更快。

我假设单元格中有额外的信息,并将grid.cells 作为字典,键是坐标的元组。如果单元格中只有坐标信息,则可以将grid.cells 作为一个集合进行类似的操作。

def get_adjacent_cells( self, x_coord, y_coord ):
    result = {}
    for x,y in [(x_coord+i,y_coord+j) for i in (-1,0,1) for j in (-1,0,1) if i != 0 or j != 0]:
        if (x,y) in grid.cells:
            result[(x,y)] = grid.cells[(x,y)]

根据您要对数据执行的操作,您可能不希望将 result 设为 dict,但希望您能理解。这应该比您的代码快得多,因为您的代码对grid.cells 中的每个单元格进行 8 次检查。

【讨论】:

  • 这种方法似乎是最快的,因为它不涉及迭代每个单元格。使用元组作为键是一个很好的选择。谢谢
【解决方案2】:

您的代码将与您的网格一样慢,因为您遍历单元格只是为了获得其中的 8 个(您已经知道它们的坐标)。

如果你可以通过他们的索引进行随机访问,我建议如下:

adjacency = [(i,j) for i in (-1,0,1) for j in (-1,0,1) if not (i == j == 0)] #the adjacency matrix

def get_adjacent_cells( self, cell ):
     x_coord = cell.x_coord
     y_coord = cell.y_coord
     for dx, dy in adjacency:
          if 0 <= (x_coord + dx) < max_x and 0 <= y_coord + dy < max_y: #boundaries check
#yielding is usually faster than constructing a list and returning it if you're just using it once
              yield grid[x_coord + dx, y_coord + dy]

max_xmax_y 应该是网格的大小,grid.__getitem__ 应该接受带有坐标的元组并返回该位置的单元格。

【讨论】:

    【解决方案3】:

    好吧,这对性能没有任何帮助,但是您可以通过说避免代码重复

    if abs(c.x_coord - x_coord) == 1 or abs(c.y_coord - y_coord) == 1:
        result.append(c)
    

    为了影响性能,您的网格单元应该知道他们的邻居是谁,可以通过c.neighbors 之类的属性,或者通过列表列表等隐式结构,以便您可以通过坐标访问。

    grid = [[a,b,c],
            [d,e,f],
            [g,h,i]]
    

    然后您可以使用列表索引检查相邻性。

    【讨论】:

      【解决方案4】:

      如果 grid.cells 被实现为一个集合,这可能是寻找邻居的最有效方法(尽管第一个 if 语句中有一个错误 - 您需要测试与 x_coord + 1 而不是 x_coord 的相等性) .

      但是,将 grid.cells 实现为列表列表将允许您按行号和列号引用单个单元格。它还允许您测量行和列的总数。然后 get_adjacent_cells 可以通过首先检查哪些边缘与当前单元格接壤,然后在所有其他方向查找邻居并将它们附加到结果列表中来工作。

      【讨论】:

        【解决方案5】:

        在网格中,邻接意味着如果我没有记错或高,您只需要任一坐标的一个步骤即可到达另一个坐标。

         if abs(c.x_coord-x_coord+c.y_coord-y_coord) == 1:
             print("They are adjacent!")
        

        【讨论】:

        • 想知道这个语句在这种情况下是否有效:(5, 5) 和 (3, 6)
        【解决方案6】:

        这适用于 numpy 数组

        def get_adjacent_cells(arr, selected_idxs):
            """
            >>> arr = np.ones((3,))
            >>> get_adjacent_cells(arr, {(1,)})
            {(0,), (1,), (2,)}
            >>> arr = np.ones((3,2))
            >>> get_adjacent_cells(arr, {(1,1)})
            {(0, 1), (1, 0), (1, 1), (2, 1)}
            >>> arr = np.ones((3,2,3))
            >>> {(0, 1, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (2, 1, 0)}
            >>> arr = np.ones((3,2,3))
            >>> get_adjacent_cells(arr, {(1,1,0), (0,1,0)})
            {(0, 0, 0), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 1, 0), (1, 1, 1), (2, 1, 0)}
            """
            w = np.asarray(list(selected_idxs))
            new_idxs = []
            for col in range(w.shape[1]):
                w_ = w.copy()
                w_[:,col] += 1
                new_idxs.extend(list(w_))
                w_ = w.copy()
                w_[:,col] -= 1
                new_idxs.extend(list(w_))
        
            new_idxs = np.array(new_idxs)
        
            # remove out of bounds coordinates
            for col, dim_size in enumerate(arr.shape):
                new_idxs = new_idxs[(new_idxs[:, col] >= 0) & (new_idxs[:, col] < dim_size)]
        
            return selected_idxs.union(map(tuple, new_idxs))
        
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-08-20
          • 2017-06-12
          • 2015-11-26
          • 1970-01-01
          • 2020-08-07
          • 1970-01-01
          • 2020-10-16
          • 1970-01-01
          相关资源
          最近更新 更多