在processing里使用80行代码制作一个元胞自动机之癌细胞扩散模型

今天研究了一下元胞自动机,在学习之前,总觉得这个名词很陌生,今天我们就来把这张纸捅破,看看到底什么是"元胞自动机"

下面是百度百科的结果:

元胞自动机(cellular automata,CA)
是一种时间、空间、状态都离散,空间相互作用和时间因果关系为局部的网格动力学模型,具有模拟复杂系统时空演化过程的能力。

通俗来说,元胞自动机就是一个盒子,这个盒子里有很多"细胞",每个"细胞"可以有自己的状态,与此同时,这些"细胞"的运动是随机的且受周围"细胞"的影响。

下面是元胞自动机的特征,了解这些特征后,你会对它有更深的认识:

  1. 同步计算(并行性):元胞自动机的处理是同步进行的
  2. 同质性:元胞空间内的每个元胞都服从相同的规则
  3. 时空局域性:
    每个元胞在t+1时刻的状态,取决于其邻居的元胞在t时刻的状态;
    t时刻的状态只对t+1时刻的状态产生影响

今天要做的模型是癌细胞的扩散模型,在写代码前,我们先来分析一下这个模型的具体内容:

  1. 制定元胞自动机的基本规则
  2. 从一个癌细胞开始,探究感染率与时间的关系

在这里插入图片描述

制定元胞自动机的基本规则

我们今天从最简单的癌细胞扩散模型开始
首先,我们先定义一个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之间随机生成一个浮点数,若该数小于癌变的概率,则让该细胞感染,具体调用方法如下,我们在后面会用到:

if (prob(tau)) cell = 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个细胞被感染成癌细胞,再往下看

第二百六十六个时间点

在这里插入图片描述
癌细胞大获全胜

声明:

  1. 这里的时间节点指一个正常细胞被完全感染的时间
  2. 该模型是比较简单的版本,实际情况中,还要考虑其他问题

以上就是全部内容,如有疑问,欢迎大家在下方评论区留言!

发布了60 篇原创文章 · 获赞 123 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/zbp_12138/article/details/105124054