今天研究了一下元胞自动机,在学习之前,总觉得这个名词很陌生,今天我们就来把这张纸捅破,看看到底什么是"元胞自动机"
下面是百度百科的结果:
元胞自动机(cellular automata,CA)
是一种时间、空间、状态都离散,空间相互作用和时间因果关系为局部的网格动力学模型,具有模拟复杂系统时空演化过程的能力。
通俗来说,元胞自动机就是一个盒子,这个盒子里有很多"细胞",每个"细胞"可以有自己的状态,与此同时,这些"细胞"的运动是随机的且受周围"细胞"的影响。
下面是元胞自动机的特征,了解这些特征后,你会对它有更深的认识:
- 同步计算(并行性):元胞自动机的处理是同步进行的
- 同质性:元胞空间内的每个元胞都服从相同的规则
- 时空局域性:
每个元胞在t+1时刻的状态,取决于其邻居的元胞在t时刻的状态;
t时刻的状态只对t+1时刻的状态产生影响
今天要做的模型是癌细胞的扩散模型,在写代码前,我们先来分析一下这个模型的具体内容:
- 制定元胞自动机的基本规则
- 从一个癌细胞开始,探究感染率与时间的关系
制定元胞自动机的基本规则
我们今天从最简单的癌细胞扩散模型开始
首先,我们先定义一个400400的空间,里面有400400个细胞,在生物学中,我们知道癌细胞是突变的细胞,在模型中,我们把时间点拖到产生癌细胞那一刻,因此,程序开始时,会随机从400*400个细胞中选择一个正常细胞,并把它变成癌细胞。这是整个程序刚开始的思路,我们来看一下细节:
int[][] beds = new int[400][400];
int x_limit = 400;
int y_limit = 400;
这里是定义空间大小,我们用一个二维数组表示,beds[0][0]表示第一行第一列的细胞,可以想象成一个沿X轴对称的二维坐标轴
color red = color(255, 0, 0); // red
color white = color(255, 255, 255); // white
int Healthy = 0;
int Infected = 1;
这里我们定义正常细胞为0,癌细胞为1:
若beds[0][0] = 0,则表示第一行第一列的细胞为正常的细胞,并标识为白色;
若beds[0][0] = 1,则表示第一行第一列的细胞为癌细胞,并标识为红色
int count = 0;
float tau = 0.5;
count用来统计时间点;tau表示癌变的概率。
boolean prob( float p )
{
if (random(0, 1) < p) return true;
else return false;
}
这里我们自定义一个函数,在0和1之间随机生成一个浮点数,若该数小于癌变的概率,则让该细胞感染,具体调用方法如下,我们在后面会用到:
下面我们在processing编辑器里编写setup初始化函数:
void setup()
{
size(400, 400);
frameRate(50);
for (int x = 0 ; x < x_limit ; x++) {
for (int y = 0 ; y < y_limit ; y++) {
beds[x][y] = Healthy;
//println("beds[" + x + "][" + y + "]:" + beds[x][y]);
stroke( white );
}
}
int lucky_x = int(random(0, 400));
int lucky_y = int(random(0, 400));
beds[lucky_x][lucky_y] = 1;
//println("beds[" + lucky_x + "][" + lucky_y + "]:" + beds[lucky_x][lucky_y]);
}
那两个println语句大家可以在自己敲的时候补上,看看它的初始化过程,第二个println语句展示了是哪个细胞癌变了
从一个癌细胞开始探究感染率与时间的关系
下面开始制定癌细胞的感染规则,假设一个正常细胞的位置是(X,Y),那么它周围的细胞里,如果有癌细胞的话,那么它自己就有被感染的风险,在上面我们也讲过了,这个风险值为0.5,我们来看看怎么表示细胞(X,Y)周围的细胞:
用代码可以这样表示:
if (cell == Healthy) {
if (beds[x][y-1]==1 || beds[x][y+1]==1 || beds[x-1][y]==1 || beds[x+1][y]==1 || beds[x-1][y+1]==1 || beds[x+1][y+1]==1 || beds[x+1][y-1]==1 || beds[x-1][y-1]==1) {
if (prob(tau)) {
cell = 1;
}
}
如果一个细胞是正常的细胞,那就去判断它周围的8个细胞,只要这8个细胞里有一个癌细胞,那么它就有被感染为癌细胞的可能,把风险带入prob函数计算,如果条件符合,则这个正常细胞变成癌细胞
变成癌细胞以后,我们还需要用颜色直观的表示出来,为了并行性,我们把感染过程和绘图分成两个函数,在绘图函数中调用感染过程的函数:
void draw()
{
update();
int sum = 0;
for (int x = 0 ; x < x_limit ; x++) {
for (int y = 0 ; y < y_limit ; y++) {
//println("beds[" + x + "][" + y + "]:" + beds[x][y]);
if (beds[x][y] == Infected) {
sum++;
stroke( red );
}
else {
stroke( white );
}
point( x, y );
}
}
println("Time:"+count);
println("Sum:"+sum);
println("--------------------");
count++;
//toDraw = (toDraw == 0) ? 1 : 0;
}
void update()
{
int x, y, cell;
//int toCompute = (toDraw == 0) ? 1 : 0;
for (x = 1 ; x < x_limit-1 ; x++) {
for (y = 1 ; y < y_limit-1 ; y++) {
cell = beds[x][y];
if (cell == Healthy) {
if (beds[x][y-1]==1 || beds[x][y+1]==1 || beds[x-1][y]==1 || beds[x+1][y]==1 || beds[x-1][y+1]==1 || beds[x+1][y+1]==1 || beds[x+1][y-1]==1 || beds[x-1][y-1]==1) {
if (prob(tau)) {
cell = 1;
}
}
beds[x][y] = cell;
}
}
}
}
核心思想就是上面讲的那些,其他代码主要是遍历每个细胞的过程,因为是二维数组,因此这里用两个for循环,想做成三维的话,就放三个for循环。
下面来看一下效果:
前两个时间点
第五个时间点
第二十六个时间点
第五十二个时间点
可以看到,癌细胞在不断扩散,并且速度越来越快 第九十个时间点
第一百二十五个时间点
第一百六十六个时间点
现在有144862个细胞被感染成癌细胞,再往下看 第二百六十六个时间点
癌细胞大获全胜
声明:
- 这里的时间节点指一个正常细胞被完全感染的时间
- 该模型是比较简单的版本,实际情况中,还要考虑其他问题
以上就是全部内容,如有疑问,欢迎大家在下方评论区留言!