【问题标题】:Efficient way of getting the neighbors in 2d numpy array在二维 numpy 数组中获取邻居的有效方法
【发布时间】:2020-12-10 17:04:53
【问题描述】:

我想将 3x3 网格中的元素与随机邻居交换。但当然有些元素有 2 个邻居,有些 3 和 1 个元素有 4 个邻居。因为我想选择一个随机邻居,所以我必须知道每个元素有多少个邻居。

我很确定我可以用很多 if else 查询来解决这个问题,但这对于这个简单的问题来说太难看了。但我对如何更优雅地做到这一点没有不同的想法。

【问题讨论】:

    标签: python arrays numpy matrix


    【解决方案1】:

    这里有两种可能的解决方案。

    在这两种情况下,都假设单元格的位置是网格中以 0 为索引的坐标(行、列)的元组。

    解决方案 1:剪辑

    不需要循环,并且有 1/n 的概率可以在 n 个可能的邻居中找到任何邻居。

    import random
    import itertools
    import numpy as np
    # All the possible moves are combination of 2 numbers among 3 (-1, 0 and 1)... 
    all_moves = list(itertools.product([-1,0,1], repeat=2))
    #... except (0,0) which is keeping the current position.
    all_moves.remove((0,0))    
    
    def random_neighbour(current_pos): 
        # Adding them to your current position gives you all virtual neighbours
        choices =  np.array(current_pos) + np.array(all_moves)
        # ... but we must remove the impossible positions
        choices = choices[((choices>=0)&(choices<=2)).all(axis=1)]
        # Now we can choose randomly among the remaining possibilities:
        return random.choice(choices)
    

    解决方案 2:字典

    这也确保我们获得相同的概率来获得任何邻居到一个单元格。在您的脚本启动时,我们可以初始化一个字典,为每个单元格(键)提供所有邻居(值)。

    import random
    neighbours_dict = {
    (p // 3, p % 3): [(p // 3 + x_inc-1, p % 3 + y_inc - 1) 
                 for x_inc in range(3) if 1 <= p // 3 + x_inc <= 3
                 for y_inc in range(3) if 1 <= p % 3 + y_inc <= 3 and not y_inc == x_inc == 1] 
    for p in range(9)}
    

    然后我们可以简单地给它当前位置的索引。请注意,位置必须是元组,因为列表不可散列且不能构成索引。所以我们将进行强制转换以确保我们不会收到 TypeError。

    random.choice(neighbours_dict[tuple(current_pos)])
    

    初始化字典的更好方法

    当然,我们也可以手动初始化字典以满足我们的特定需求,像这样:

    neighbours_dict = {(0, 0): [(0, 1), (1, 0), (1, 1)],
     (1, 0): [(0, 0), (0, 1), (1, 1), (2, 0), (2, 1)],
     (2, 0): [(1, 0), (1, 1), (2, 1)],
     (0, 1): [(0, 0), (0, 2), (1, 0), (1, 1), (1, 2)],
     (1, 1): [(0, 0), (0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1), (2, 2)],
     (2, 1): [(1, 0), (1, 1), (1, 2), (2, 0), (2, 2)],
     (0, 2): [(0, 1), (1, 1), (1, 2)],
     (1, 2): [(0, 1), (0, 2), (1, 1), (2, 1), (2, 2)],
     (2, 2): [(1, 1), (1, 2), (2, 1)]}
    

    或者我们可以概括字典理解,使其兼容任何网格形状和大小:

    grid_width, grid_height = 3, 3
    neighbours_dict = {
        (p // grid_width, p % grid_width):
            [(p // grid_width + y_inc - 1, p % grid_width + x_inc - 1) 
                for y_inc in range(3) if 1 <= p // grid_width + y_inc <= grid_height
                for x_inc in range(3) if 1 <= p % grid_width + x_inc <= grid_width 
                        and not y_inc == x_inc == 1] 
        for p in range(grid_width * grid_height) }
    

    对我来说,后者是其中最优雅的解决方案。

    【讨论】:

      【解决方案2】:

      将 3x3 网格嵌入到 5x5 网格中。在边界中,有一些永远不会出现在 3x3 网格中的值,比如 -1。

      -1 -1 -1 -1 -1 
      -1          -1
      -1 Your Grid-1
      -1          -1
      -1 -1 -1 -1 -1
      

      现在,每个内部网格单元都有 4 个邻居。对于这些单元格,请进行拒绝采样。那就是不断地从附近循环一个样本,直到你抽取一个不是 -1 的样本。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-05-06
        • 2017-12-23
        • 2021-05-23
        • 1970-01-01
        • 2010-10-13
        • 1970-01-01
        • 2017-10-04
        相关资源
        最近更新 更多