元胞自动机 ¶
约 1033 个字 54 行代码 预计阅读时间 4 分钟
写在前面
刚刚结束了工高的第二轮面试——实践面试,整一个面试是要求一个团队一起合作在三天内完成几道数学建模题,过程特肝,我真的可能把每道题的分析都码上来了。所以我就简要的提一下其中接触到的知识吧。今天,我就来简要说说元胞自动机的一些知识吧。
本文写于 2020 年 3 月 28 日,现整理收录。
元胞自动机(CA)
它是一种时间、空间、状态都离散的,空间相互作用和因果关系为局部的网格动力学模型。其中重要的部分是规则和邻居,这两个要素决定了元胞自动机的演化过程。其最出名的便是非常流行的“生命游戏”。
一维元胞自动机 ¶
为了简化,所以从简单的一维元胞自动机说起。
其是由一行元胞构成,每个元胞的状态有 1 或 0 两个状态 ,这整一行元胞就构成了一个元胞自动机。其下一代的演化方式是由每个元胞相邻的 N 个元胞状态和自身的状态形成的 N+1 邻域共同决定。
当 N=2 时,即每个元胞和其相邻的左右两个元胞的状态决定下一代的状态,具体如何生成呢,这里先用一种简单的规则加以展示。
很容易得到,3 邻域的元胞状态总共有 2^3=8 种状态,如下:
000 001 010 011 100 101 110 111
其每一个状态都决定下一代状态,如
0 0 0 0 1 1 1 1
那么规则就可以以00001111
来表示,称其为规则数。
以上就是一种产生规则,但是我们很快会发现这样会遇到一个问题,在两端的会出现相邻元胞数不足的现象。此时一般会采用首尾相连的方式来解决。
投票机 ¶
为了更好的理解元胞自动机,这里用一个简单的案例来展示。
一个有 n 个元胞的一维元胞自动机,能较好实现“少数服从多数”的任务:若输入(t0 = 0)中 状态为 1 的元胞多于状态为 0 的元胞,则经过 t 次运算后所有元胞状态为 1;同理,若输入(t0 = 0) 中状态为 0 的元胞多于状态为 1 的元胞,则经过 t 次运算后所有元胞状态为 0。请制定规则,使得适应度尽可能的高。
根据上面的解释,大家可以自行尝试。
这里我给出最优解法:
选取 7 邻域作为决定下一代的状态组,这样规则数就有 2^7=128 位 规则数:
00000101000001100001010110000111000001110000010000010101010101110110010001110111 000001010000000101111101111111111011011101111111
接下来既可以采取首尾相连的方式也可以,稍微与元胞自动机定义有所差别的整体优势补全法,即用上一代状态中占优势的状态来补齐首尾。
之所以有所不妥,是因为这里有点类似 [^ 冯诺依曼机 ] 的一步到位的想法,但是采取这样的方法可以使适应度达到 100%,所以这里就采用了整体优势补全的方法。
Python 实现代码
from random import randint
def getBirth(N):
f=-1
while f==-1: #避免产生1和0数目相同的初始状态
s=''
for i in range(N):
s+=str(randint(0,1))
if s.count('0')>s.count('1'):
f=0
elif s.count('0')==s.count('1'):
f=-1
else:
f=1
return s,f
def getNext(n,f):
rule='0000010100000110000101011000\
011100000111000001000001010101\
010111011001000111011100000101\
0000000101111101111111111011011101111111'
nextGeneration=''
N=len(n)
for i in range(N):
#边缘单独考虑补全
if i<3:
s=str(f)*(3-i)+n[0:i]+n[i:i+4]
elif i>N-4:
s=n[i-3:i]+n[i:N]+str(f)*(3-(N-1-i))
else:
s=n[i-3:i]+n[i:i+4]
s=int(s,2)
nextGeneration+=rule[s]
return nextGeneration
def main():
num=0; #记录成功次数
datas=200 #测试数据总数
for i in range(datas):
N=100 #序列长度
t=0 #记录时间
n,f=getBirth(N) #生成初始状态
while t<300: #超过300次,默认判定无法成功
n=getNext(n,f)
t+=1
if (f==0 and n.count('0')==N) or (f==1 and n.count('1')==N): #判断是否成功
num+=1
break
# 输出结果
print('datas sum:',datas)
print(int(num/datas*100),'%')
if __name__=='__main__':
main()
input()
随便说说 ¶
这里我就暂时说到这里了 主要太懒了,不想码字了,至于规则是如何产生的,各位可以去参考遗传算法,我在这里也推荐一本书,讲的挺简单的。
《复杂》 梅拉妮 · 米歇尔
《Complexity: A Guided Tour》 Melanie Mitchell
这上面是一本书哦,只是中英文的区别 ~
写在最后 ¶
其实元胞自动机本身并不复杂,但是可以模拟很多复杂的现象,在这次实践面试中还实现了长三角城市建成区的模拟、还有教室逃生的模拟等等,有兴趣的话,可以深入了解。
这次讲的很浅,等我有兴趣码字了,再详细展开讲讲(逃