跳转至

元胞自动机

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

这上面是一本书哦,只是中英文的区别 ~

写在最后

其实元胞自动机本身并不复杂,但是可以模拟很多复杂的现象,在这次实践面试中还实现了长三角城市建成区的模拟、还有教室逃生的模拟等等,有兴趣的话,可以深入了解。

这次讲的很浅,等我有兴趣码字了,再详细展开讲讲(逃