在纯 numpy 中矢量化康威的生命游戏?

Vectorize Conway's Game of Life in pure numpy?

我想知道是否有一种方法可以在不诉诸 for 循环、if 语句和其他典型的编程控制结构的情况下实现 Conway 的人生游戏。 向量化 for 循环应该很容易,但是如何将对邻域的检查转换为矩阵运算?

基本逻辑类似于this:

def neighbors(cell, distance=1):
    """Return the neighbors of cell."""
    x, y = cell
    r = xrange(0 - distance, 1 + distance)
    return ((x + i, y + j) # new cell offset from center
            for i in r for j in r # iterate over range in 2d
            if not i == j == 0) # exclude the center cell

我希望这不会被 mods 认为是题外话,我真的很好奇,我才刚刚开始使用 CA。

干杯

您的问题的答案是 "yes, it is possible"(尤其是从第 n 版更新到第 n+1 版的版)。

我把过程描述的很详细here. The main technique to generate the neighborhood around a central cell involves using "strides" (the way that numpy and other array computation systems know how to walk across rows and columns of elements when they are really stored in memory in flat 1D thing) in a custom fashion to generate neighborhoods around cells. I describe that process here

最后一条评论:由于 Game of Life 从状态 n 迭代到状态 n+1,而您 可以 从字面上删除所有命令式循环,这实际上没有意义去掉那个顶层控制回路。所以,有一个循环: for round in range(num_rounds): board.update() 其中 board.update 不使用循环(除了做一些边计算......同样,你 可以 删除那些但它会使程序更长,更不优雅)。

为了给你一个具体的例子(并且更符合 Whosebug 的回答要求),这里有一些 select 从我的帖子中剪切和粘贴以从一个简单的 4x4 板生成中心街区 [抱歉,这是python 2 代码,你必须稍微修改 prints]:

board = np.arange(16).reshape((4,4))
print board
print board.shape

我们要挑出以 5、6、7、8 为中心的四个 "complete" 邻域。让我们看一下 5 的邻域。结果的形状是什么? 3×3。进展如何?好吧,穿过一排仍然只是一次走一个元素,而到达下一行仍然是一次 4 个元素。这些与原版中的步幅相同。不同之处在于我们不采用 "everything",我们只采用 selection。让我们看看这是否真的有效:

from numpy.lib.stride_tricks import as_strided
neighbors = as_strided(board, shape=(3,3), strides=board.strides)
print neighbors

好的,很好。现在,如果我们想要所有四个邻域,输出形状是什么?我们有几个 3×3 的结果。多少?在这种情况下,我们有 2×2 个(每个 "center" 单元格)。这给出了 (2,2,3,3) 的形状——邻域是内部维度,邻域的组织是外部维度。

因此,我们的步幅(就元素而言)最终在一个社区内为 (4,0),而在邻里之间进展为 (4,0)。总步幅(元素方面)是:(4,0,4,0)。但是,组件步幅(我们的外部二维)与板的步幅相同。这意味着我们的邻域步幅是 board.strides + board.strides.

print board.strides + board.strides

neighborhoods = as_strided(board, 
                           shape=(2,2,3,3), 
                           strides=board.strides+board.strides)

print neighborhoods[0,0]
print neighborhoods[-1, -1]