尝试优化我当前使用Wolfram编号生成细胞自动机的程序的实现。在计算每个单元的邻居后,我遇到了将规则应用于董事会的麻烦。当前的例子使用了2种状态,与Conway的生命游戏相同,但我的程序可以处理任意数量的状态。小数224以以下方式对应于CGOL的规则集:(0, 0, 0, 0, 0, 1, 1, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)基本上,每个状态有18个位置,或者9个可能的邻域和(0-8)。如果当前单元格为1,则按以下方式索引该规则:
>>> [0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0][1::2]
[0, 0, 1, 1, 0, 0, 0, 0, 0]
1为单元格的值,2为状态数。如你所见,如果状态是1,那么如果有2或3个邻居细胞存活,否则死亡。从那里,你索引w/该单元格的邻域和,以获得该单元格的实际更新值。因此,要在每一代更新一个单元格,您需要:规则[state_value::total_states][邻居的总和]。例如,
>>> [0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0][1::2][2]
1
目前,我有一个任意形状的所有细胞的网格,称为world,另一个同样形状的numpy数组,其中每个细胞的所有邻居的总和使用scipy的卷积计算-称为nbr -,以及前面提到的规则列表-是否有可能更新世界中每个细胞的值,同时避免for循环?
例如:World = rule[cell_value::total_states][sum_of_neighbors_for_given_cell_in_nbrs]
您没有给我们很多代码,所以这里有一个关于它如何工作的最小想法。
首先,创建一个数组,您可以使用当前状态对其进行索引,以获得规则:
rule = [0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0]
rule_map = np.stack((rule[0::2], rule[1::2]), axis=0)
现在您可以使用当前状态来获取每个单元格的规则:
world = np.random.randint(2, size=(5, 6))
cur_cell_rules = rule_map[world] # shape: (5, 6, 9)
要获得新的世界状态,可以使用索引交互器。在这里,我使用一个包含所有世界索引的数组来首先获得(扁平的)当前单元的邻域和,然后使用这些来获得(扁平的)新状态。在作业中,我再次将其平展为世界形状。(可能有更简单的方法…)
cur_cell_neighborhood_sum = ... # shape: (5, 6)
world_ind = np.asarray([*np.ndindex(world.shape)])
# update world
world[world_ind[:, 0], world_ind[:, 1]] = cur_cell_rules[world_ind[:, 0], world_ind[:, 1], cur_cell_neighborhood_sum[world_ind[:, 0], world_ind[:, 1]]]
编辑:
要避免使用较大的cur_cell_rules
数组,您也可以采用另一种方式:
world_potential = rule_map[:, cur_cell_neighborhood_sum] # shape: (2, 5, 6)
# update world, this time in smaller steps
world_flat = world[world_ind[:, 0], world_ind[:, 1]]
world_new_flat = world_potential[world_flat, world_ind[:, 0], world_ind[:, 1]]
world[world_ind[:, 0], world_ind[:, 1]] = world_new_flat