【问题标题】:Find a nodes 8 neighbors on a 2D grid在 2D 网格上查找节点 8 个邻居
【发布时间】:2015-12-01 09:02:26
【问题描述】:

我需要在二维数组中找到任何节点的基数和对角邻居。取下面的数组。我正在从数组上的 [1, 1] 1 中搜索。它应该返回周围的 0。我可以找到刚刚找到的邻居,如下面的一些代码所示,但它们太慢了。

[[0, 0, 0]
 [0, 1, 0]
 [0, 0, 0]]

我可以像这样强制它返回所有 8 个周围节点的坐标。

def ReturnNeighbors(x, y):
    numpy.array([(x-1, y), (x, y-1), (x+1, y), (x, y+1),
               (x-1, y-1), (x+1, y+1), (x-1, y+1), (x+1, y-1)])

def ReturnNeighbors_4(x, y):
    for i in xrange(x - 1, x + 2):
        for j in xrange(y - 1, y + 2):
            if (i, j) != (x, y):
                yield (i, j)

或者通过计算到所有节点的距离并返回距离

我在切换到 numpy 之前处理这个问题的方式是我的数组中充满了实际的节点对象作为值。每个对象都将其 x 和 y 坐标存储在网格和一组相邻对象中。像这样。我不能再使用这种方法了,因为填充一个充满节点的巨大数组需要很长时间。即使在一个包含 10000 个节点对象的 100x100 小地图中!我计划以后不仅有更大的地图,而且在任何给定时间都有多个地图处于活动状态。由于节点印记,我实际上已经用完了内存试图创建更大的地图。它适用于小地牢,但不适用于模拟多张地图的世界。

ExampleNode(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.neighbors = set()

        # The neighbors are found and added to the above set later.

node = Example(0, 0)

for neighbor in node.neighbors:
    do thing with each neighbor

出于各种原因,我需要一个节点邻居。使用元胞自动机平滑地图、在战斗中在附近溅血、使用广度优先搜索进行寻路等等。这是我正在创建的roguelike。蛮力方法适用于小型 60x60 地牢,但现在我正在扩大范围并生成世界地图。它只有 200x200,而且速度非常慢。

【问题讨论】:

  • 数组中总是只有 0 还是 1?您需要这 8 个邻居的索引还是只需要元素本身?
  • 这个数组充满了大量不同的整数。 0 是土地,1 是水,2 是树木等等。我需要 8 个邻居的坐标。
  • 我们如何处理边界条件,即在没有 8 个邻居的边界处为 1。当两个 1 共享邻居时,我们是否还应该为每个旋转的 1 报告重叠的邻居两次?
  • 如果他们不是 8 个邻居,那么它应该只返回有多少邻居可用。就像在数组的最边缘它应该返回 5 个邻居或在角落它应该返回 3,因为其他邻居不存在超出边界。第二个问题有点超出我的理解。我不确定你所说的重叠和旋转是什么意思。
  • 我假设您在输入数组中 1 的所有位置的所有 X 和 Y 上运行 ReturnNeighbors func。因此,按照这个假设,现在假设您在输入数组中有一堆1s,并且您希望其中每个 1 有八个邻居。然后,如果你有几个 1 足够接近,他们的邻居可能是相同的,所以我们仍然应该报告他们吗?此外,如果假设成立,我建议发布一个通用示例案例,其边界为 1,并且足够接近一堆 1,并以您希望的格式发布预期输出。

标签: python arrays numpy 2d nearest-neighbor


【解决方案1】:

假设输入数组名为A,其中包含所有整数,我们正在处理其中的1s 元素并尝试获取它们的邻居。这是这样做的一种方法-

# Get offsets for row and column
R_offset,C_offset = np.meshgrid(np.arange(-1,2),np.arange(-1,2))

# Get row and column indices for places where elements are 1s
R_match,C_match = np.nonzero(A==1)

# Store number of matches as it would be frequently used
N = R_match.size

# Get offsetted row, col indices for all matches
R_idx = (R_match[:,None,None] + R_offset).reshape(N,-1)
C_idx = (C_match[:,None,None] + C_offset).reshape(N,-1)

# Based on boundary conditions set invalid ones to zeros
valid_mask = (R_idx>=0) & (C_idx>=0) & (R_idx<A.shape[0]) & (C_idx<A.shape[1])
valid_mask[:,4] = 0 # Set the pivot(self/center) ones to invalid

# Using valid mask, "cut off" elements from each group of 9 elems
cut_idx = valid_mask.sum(1).cumsum()

# Finally form groups
grps_R = np.split(R_idx[valid_mask],cut_idx)[:-1]
grps_C = np.split(C_idx[valid_mask],cut_idx)[:-1]

示例运行和解释如何解释和使用输出 -

In [256]: A
Out[256]: 
array([[1, 0, 0, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 1, 0, 0],
       [0, 0, 0, 0, 1]])

In [257]: grps_R
Out[257]: [array([1, 0, 1]), array([1, 2, 3, 1, 3, 1, 2, 3]), array([2, 3, 2])]

In [258]: grps_C
Out[258]: [array([0, 1, 1]), array([1, 1, 1, 2, 2, 3, 3, 3]), array([3, 3, 4])]

因此,我们基于A 中的三个1s 分为三个组。

第一个在左上角-

In [259]: np.column_stack((R_match[0],C_match[0]))  # First point
Out[259]: array([[0, 0]])

In [260]: np.column_stack((grps_R[0],grps_C[0]))    # Its three neighbors
Out[260]: 
array([[1, 0],
       [0, 1],
       [1, 1]])

第二个是(2,2) -

In [263]: np.column_stack((R_match[1],C_match[1]))  # Second point 
Out[263]: array([[2, 2]])

In [264]: np.column_stack((grps_R[1],grps_C[1]))    # Its eight neighbors
Out[264]: 
array([[1, 1],
       [2, 1],
       [3, 1],
       [1, 2],
       [3, 2],
       [1, 3],
       [2, 3],
       [3, 3]])

最后第三个在(4,5) -

In [265]: np.column_stack((R_match[2],C_match[2]))  # Third point 
Out[265]: array([[3, 4]])

In [266]: np.column_stack((grps_R[2],grps_C[2]))    # Its three neighbors
Out[266]: 
array([[2, 3],
       [3, 3],
       [2, 4]])

【讨论】:

  • 这很棒。它为我的元胞自动机相关的东西带来了巨大的速度提升!我不认为它解决了我的问题,除非我不明白如何使用它。如果我理解正确,这将返回特定类型的所有值的所有邻居。换句话说,它给了我所有 1 的邻居。不过,我只需要一个非常特定节点的邻居。我想将 [x, y] 坐标传递给函数并返回该坐标邻居。答案对改进我的代码的其他部分的帮助从来没有减少过,所以谢谢。
  • @Patwollip 我猜在那个节点情况下,您可以将R_match,C_match = np.nonzero(A==1) 编辑为R_match,C_match = x,y。试试看?
  • 它在进行更改后抱怨N = R_match.size,说明 AttributeError "int" object has no attribute "size"。我想这是有道理的,因为节点 x,y 坐标不再与 numpy 相关。
  • @Patwillip 是的,我想你可以使用N = len(R_match)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-08-20
  • 1970-01-01
  • 2011-02-10
  • 2019-11-17
  • 2015-11-26
  • 2020-01-29
  • 2012-11-10
相关资源
最近更新 更多