我想用一个简单的规则来实现一维元胞自动机:
例如:
0 1 1 0 1
更新后:
1 1 0 1 0
我的解决方案是
def update(cells):
neighbors = np.roll(cells,-1)
dim = len(cells)
tmp_cells = np.zeros(dim)
for i,j in enumerate(cells):
if j and not neighbors[i]:
tmp_cells[i], tmp_cells[(i+1)%dim] = 0, 1
elif j:
tmp_cells[i] = 1
return tmp_cells
这很好,但解决方案没有利用np.arrays
的所有可能性,而是简化为list
- 算法。
我以为我可以在cells
和neighbors
之间找到一个简洁的逻辑,但显然我现在必须睡觉了。
一些想法?
答案 0 :(得分:5)
要获取没有循环的单元格的值,您需要知道两个侧的邻居。您需要左侧,因为如果您是0
,则新值取决于您的左邻居,而如果您是1
,则新值取决于您的右邻居。
你可以详尽地写出所有3个细胞的组合,对吧?换句话说:
000 -> 0
001 -> 0
010 -> 0 # move to the right
011 -> 1 # stay put
100 -> 1 # left neighbor has moved
101 -> 1 # left neighbor has moved
110 -> 0 # move to the right
111 -> 1 # stay put
您可以非常轻松地将该表转换为布尔函数。我们可以简化它,但让我们开始时只是愚蠢:-x & y & z | x & -y & -z | x & -y & z | x & y & z
。
就是这样:
left = np.roll(cells, -1)
right = np.roll(cells, 1)
return (np.logical_not(left) & cells & right | # ...)
当然,你现在想要简化布尔方程,*但这应该让你开始。
*或者可以退一步重新考虑规则。如果您是0
,则始终会从您的左邻居复制新值;如果你是1
,它总是从你的右邻居复制。您可以使用布尔运算符的组合来编写它,但使用屏蔽赋值可能更简单:result[cells] = left[cells]; result[notcells] = right[notcells]
。
答案 1 :(得分:1)
为了获得最佳性能,最好使用视图。像这样:
import numpy as np
cells = [0, 1, 1, 0, 1]
cells = np.concatenate([[0],cells, [0]]) #add padding cells
def boundary(cells):
"""enforce boundary conditions"""
cells[0] = cells[-2]
cells[-1] = cells[1]
return cells
cells = boundary(cells)
centre = cells[1:-1]
left = cells[0:-2]
right = cells[2:]
#add your logic here...
print (np.logical_not(left) & centre & right)
答案 2 :(得分:0)
基于 Eelco Hoogendoorn 关于观看效果的评论以及 abarnet 的重要逻辑提示,这里的解决方案对我有用:
def update_parallel(cells):
cells = boundary(cells)
center = cells[1:-1]
left = cells[0:-2]
right = cells[2:]
ones = (center == 1)
zeros = (center == 0)
result = np.copy(center)
result[zeros] = left[zeros]
result[ones] = right[ones]
num_moves = sum(np.logical_xor(center, result)) / 2 # number of moves per step
return result, num_moves