【问题标题】:Python - comparing elements of list with 'neighbour' elementsPython - 将列表元素与“邻居”元素进行比较
【发布时间】:2013-05-13 19:39:40
【问题描述】:

这可能更像是一个“方法”或概念问题。

基本上,我有一个像这样的多维列表的python:

my_list = [[0,1,1,1,0,1], [1,1,1,0,0,1], [1,1,0,0,0,1], [1,1,1,1,1,1]]

我要做的是遍历数组并将每个元素与直接围绕它的元素进行比较,就好像列表被布置为矩阵一样。

例如,给定第一行的第一个元素my_list[0][0],我需要知道my_list[0][1]my_list[1][0]my_list[1][1] 的值。 “周围”元素的值将决定当前元素的操作方式。当然,对于数组中心的元素,需要进行 8 次比较。

现在我知道我可以简单地遍历数组并与索引值进行比较,如上所述。我很好奇是否有更有效的方法来限制所需的迭代量?我应该按原样迭代数组,还是迭代并仅比较任一侧的值,然后转置数组并再次运行它。然而,这会忽略对角线的那些值。我是否应该存储元素查找的结果,这样我就不会多次确定同一个元素的值?

我怀疑这可能是计算机科学中的一种基本方法,我渴望获得有关使用 Python 的最佳方法的反馈,而不是为我的问题寻找具体答案。

【问题讨论】:

  • 你可以在这里使用numpy吗?因为它充满了以自然的类似矩阵的方式进行类似矩阵运算的方法(通常比纯 Python 快一个数量级)。
  • 无论如何,就算法复杂性而言:在单个(两级)通道中迭代矩阵,同时在每一步检查 3-8 个周围元素,是 O(N*M),这是你能做的最好的。那么,这有什么问题呢?
  • 没错,但矩阵运算可以在您的 GPU 上完美并行化。这样可以节省很多时间!
  • @RouvenB.:是的,numpy 也节省了大量时间……或者只是在 PyPy 而不是 CPython 下运行代码,或者使用更快的计算机。这就是为什么我先给出我的第一个评论,然后用单独的评论回答“计算机科学的基本方法”部分。

标签: python arrays list


【解决方案1】:

您可以通过使用numpy 或其他替代方法(详见下文)获得更快甚至更简单的代码。但是从理论的角度来看,就算法复杂度而言,你能得到的最好的结果是 O(N*M),你可以用你的设计来做到这一点(如果我理解正确的话)。例如:

def neighbors(matrix, row, col):
    for i in row-1, row, row+1:
        if i < 0 or i == len(matrix): continue
        for j in col-1, col, col+1:
            if j < 0 or j == len(matrix[i]): continue
            if i == row and j == col: continue
            yield matrix[i][j]

matrix = [[0,1,1,1,0,1], [1,1,1,0,0,1], [1,1,0,0,0,1], [1,1,1,1,1,1]]

for i, row in enumerate(matrix):
    for j, cell in enumerate(cell):
        for neighbor in neighbors(matrix, i, j):
            do_stuff(cell, neighbor)

这需要 N * M * 8 个步骤(实际上,比这要少一些,因为许多单元格的邻居数少于 8 个)。从算法上讲,没有办法比 O(N * M) 做得更好。所以,你已经完成了。


(在某些情况下,您可以通过考虑迭代器转换来使事情变得更简单(无论哪种方式都不会显着改变性能)。例如,您可以轻松地在列表 a 中的相邻三元组上创建一个分组器正确压缩aa[1:]a[2:],您可以将其扩展到相邻的二维非整数。但我认为在这种情况下,它只会使您的代码比编写显式neighbors 迭代器更复杂并且显式的for 循环遍历矩阵。)


但是,实际上,您可以通过各种方式获得更快的速度。例如:

  • 使用numpy,您可能会获得一个数量级或更快的速度。当您迭代一个紧密的循环并进行简单的算术运算时,这是 Python 特别慢的事情之一,而 numpy 可以用 C(或 Fortran)代替。
  • 使用您最喜欢的 GPGPU 库,您可以显式矢量化您的操作。
  • 使用multiprocessing,您可以将矩阵分解为多个部分,并在单独的内核(甚至单独的机器)上并行执行多个部分。

当然,对于单个 4x6 矩阵,这些都不值得做……除了numpy,它可以让你的代码更简单也更快,只要你能用矩阵/广播术语自然地表达你的操作.

事实上,即使你不能用这种方式轻松表达,仅仅使用numpy存储矩阵可能会让事情变得更简单(如果重要的话,还可以节省一些内存) .例如,numpy 可以让您自然地访问矩阵中的单个列,而在纯 Python 中,您需要编写类似 [row[col] for row in matrix] 的内容。


那么,您将如何使用 numpy 解决这个问题?

首先,您应该阅读numpy.matrixufunc(或者,更好的是,一些更高级别的教程,但我没有推荐的),然后再深入了解。

无论如何,这取决于您对每组邻居所做的事情,但有三个基本想法。

首先,如果您可以将运算转换为简单的矩阵数学,那总是最简单的。

如果没有,您可以通过在每个方向上移动矩阵来创建 8 个“邻居矩阵”,然后对每个邻居执行简单的操作。在某些情况下,从 N+2 x N+2 矩阵开始可能更容易,在外缘具有合适的“空”值(通常为 0 或 nan)。或者,您可以移动矩阵并填充空值。或者,对于某些操作,您不需要相同大小的矩阵,因此您可以裁剪矩阵以创建邻居。这真的取决于你想做什么操作。

例如,将您的输入作为Game of Life 的固定 6x4 板:

def neighbors(matrix):
    for i in -1, 0, 1:
        for j in -1, 0, 1:
            if i == 0 and j == 0: continue
            yield np.roll(np.roll(matrix, i, 0), j, 1)

matrix = np.matrix([[0,0,0,0,0,0,0,0],
                    [0,0,1,1,1,0,1,0],
                    [0,1,1,1,0,0,1,0],
                    [0,1,1,0,0,0,1,0],
                    [0,1,1,1,1,1,1,0],
                    [0,0,0,0,0,0,0,0]])
while True:
    livecount = sum(neighbors(matrix))
    matrix = (matrix & (livecount==2)) | (livecount==3)

(请注意,这不是解决此问题的最佳方法,但我认为它相对容易理解,并且可能会阐明您的实际问题。)

【讨论】:

  • +1 表示 numpy。我只是在阅读了您答案的顶部之后才提到它。
  • @forivall:也许我应该重新组织一下答案。让我尝试。感谢您的评论。
  • 你的回答组织得很好,我只是同意你的看法。
  • 感谢您的精彩回答。我预计 numpy 会很合适,但我想让它保持打开状态。就 matrx 复杂度而言,我知道我的最大尺寸为 50x50。你会考虑使用 numpy 来获得这种规模的 martix 的显着优势吗?
  • @DarwinTech:在您的情况下,了解“周围元素的值将决定当前元素的操作方式”会有所帮助,但我想出了我希望的简单而著名的例子:生命游戏的规则,对于每个单元格,计算非零相邻单元格的数量,并使用它来确定如何转换单元格的值。
猜你喜欢
  • 2017-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-17
  • 2013-06-06
  • 1970-01-01
相关资源
最近更新 更多