写在前面
现有的科学研究显示,生命形式由单细胞开始。中学时在实验课上借着显微镜看细胞,看着镜中一个个排列规整,“生机勃勃”的细胞,会想象它们在”细胞群”中度过的一生,那么我们可不可以建立一个简单的模型来模拟一下,看看它们可能的演变过程呢。
Part I
那我们来建个模吧。
为了简化问题
我们考虑将每个生命(细胞)简化为方格平面上的一格
唰!我们割开一个二维平面,然后将平面划分成一个个小格子
上图中黑色格子就是我们简化的细胞模型
在放置完细胞后,下一步做什么?
神说,要有光。(划去)
我们来考虑这样一些问题
当时间开始走动起来的时候,
细胞本身
- 在什么条件下能够存活
- 在什么条件下能够繁衍后代,以及后代存在的位置
- 在什么条件下死亡
我们知道生命是要和外界周围发生联系的
所以我们把四周八个格子称为中心格子的邻居,纳入考量
我们感性地可以制定下面的规则
- 任何活细胞如果活邻居少于2个,则死掉。(“人口”过少)
- 任何活细胞如果活邻居为2个或3个,则继续活。(正常)
- 任何活细胞如果活邻居大于3个,则死掉。(”人口“过多)
- 任何"空地"如果活邻居正好是3个,则活过来。(繁殖)
诶等等,就这样的几个规则?
说好的生命游戏呢?四个规则能玩出什么花来吗?
Part II
我们来看看这简单的四个规则究竟能撞出怎样的火花吧。
根据Part1中制定的规章
我们可以用C++编制一个简单的生命游戏
//生命游戏核心代码
#define MAXN 10
#define KEEP 2
#define BORN 3
struct Cell{
bool live;
int others;
}c[MAXN|1][MAXN|1];
inline void judge(){
for(int i=1;i<=MAXN;i++)
for(int j=1;j<=MAXN;j++){
c[i][j].others=0;
if(c[i-1][j].live&&i-1>0) c[i][j].others++;
if(c[i+1][j].live&&i+1<MAXN) c[i][j].others++;
if(c[i][j-1].live&&j-1>0) c[i][j].others++;
if(c[i][j+1].live&&j+1<MAXN) c[i][j].others++;
if(c[i-1][j-1].live&&i-1>0&&j-1>0) c[i][j].others++;
if(c[i+1][j-1].live&&i+1<MAXN&&j-1>0) c[i][j].others++;
if(c[i-1][j+1].live&&i-1>0&&j+1<MAXN) c[i][j].others++;
if(c[i+1][j+1].live&&i+1<MAXN&&j+1<MAXN) c[i][j].others++;
}
suriver=0;
for(int i=1;i<MAXN;i++)
for(int j=1;j<MAXN;j++){
switch(c[i][j].others){
case BORN: c[i][j].live=true;suriver++; break;
case KEEP: if(c[i][j].live)suriver++;break;
default: c[i][j].live=false; break;
}
}
}
如果还不会编制C++代码,可以下载更为完善的Golly进行模拟
我们先来试试构建的这个模型吧
随机构造一些坐标放置细胞,如下
5 5
4 5
3 5
1 2
1 1
2 3
3 2
3 5
6 5
3 4
2 7
6 3
4 7
6 2
4 7
-1 -1
得到
根据上面简单的演示我们可以看到,仅仅只是使用了四个简单的规则,和一些杂乱无章的初始活细胞位置,也能够演化出复杂多变的局面。
事实上
通过不断的测试,人们发现了更多有意思的特殊”生命形式“
会不断行进的”滑翔者“
可以源源不断产生滑翔者的”滑翔者“枪
甚至还有可以不断行进并产生”滑翔者“的”繁殖者“
它会向右行进,留下一个接一个的“滑翔者枪”。动图最后一帧定格时用三种颜色区分了繁殖者本体、滑翔者枪和它们打出来的滑翔者
Part 3
事实上,我们建模的过程和英国数学家约翰·何顿·康威在1970年研究元胞自动机的过程如出一辙,因此该规则下的”生命游戏“也被称之为康威生命游戏。
仅仅是简单的四个规则,我们放置下的细胞们也可以演化出多样的结构。
那么我们所处的真实宇宙的规则,最终也会是如此的简单优雅吗?
开发出著名科学计算软件Mathematica的斯蒂芬·沃尔夫勒姆(Stephen Wolfram)曾如此说:
宇宙的本质是计算
或许这句话听来过于”武断“,但斯蒂芬的判断说明了,既然不含随机性的细胞自动机也可以产生”无法预测“的模式,我们生活中所存在的”不确定性“,可能也是根据”不含随机性“的有限规则计算产生的”确定结果“。
写在后面
关于元胞自动机的扩展阅读资料:https://www.guokr.com/article/439770/
部分图源来自:Life Wiki https://www.conwaylife.com/wiki/
附上自己编的生命游戏程序,另托管于Github,可点原文查看
#include<cstdio>
#include<Windows.h>
#define SLEEPTIME 500//刷新时间
#define MAXN 50//地图边长
#define KEEP 2//不死最少需细胞
#define BORN 3//产生最少需细胞
struct Cell{
bool live;
int others;
}c[MAXN|1][MAXN|1];
int round=0,suriver=0;
void HideCursor() {
CONSOLE_CURSOR_INFO cursor_info = {1, 0};
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
// 更改窗口大小,自适应
void ChangeWindow() {
const int columns = 2*MAXN;
const int rows = MAXN;
char ch[100];
int size = sprintf(ch,"mode con cols=%d lines=%d",columns,rows);
ch[size]='\0';
system(ch);
HideCursor();
}
inline void init(){
int x,y;
while(1){
scanf("%d %d",&x,&y);
if(x>0&&x<=MAXN&&y>0&&y<=MAXN)
c[x][y].live=true;
else if(x==-1&&y==-1) return ;
else printf("NO EXIST!\n");
}
fclose(stdin);
}
//根据规则执行的局面判断
inline void judge(){
for(int i=1;i<=MAXN;i++)
for(int j=1;j<=MAXN;j++){
c[i][j].others=0;
if(c[i-1][j].live&&i-1>0) c[i][j].others++;
if(c[i+1][j].live&&i+1<=MAXN) c[i][j].others++;
if(c[i][j-1].live&&j-1>0) c[i][j].others++;
if(c[i][j+1].live&&j+1<=MAXN) c[i][j].others++;
if(c[i-1][j-1].live&&i-1>0&&j-1>0) c[i][j].others++;
if(c[i+1][j-1].live&&i+1<=MAXN&&j-1>0) c[i][j].others++;
if(c[i-1][j+1].live&&i-1>0&&j+1<=MAXN) c[i][j].others++;
if(c[i+1][j+1].live&&i+1<=MAXN&&j+1<=MAXN) c[i][j].others++;
}
suriver=0;
for(int i=1;i<=MAXN;i++)
for(int j=1;j<=MAXN;j++){
switch(c[i][j].others){
case BORN: c[i][j].live=true;suriver++; break;
case KEEP: if(c[i][j].live)suriver++;break;
default: c[i][j].live=false; break;
}
}
}
inline void draw(){
ChangeWindow();
for(int i=1;i<=MAXN;i++){
for(int j=1;j<=MAXN;j++){
if(c[i][j].live) printf("■");
else printf("□");
}
//printf("\n");
}
printf(" round:%d suriver:%d",round,suriver);
}
int main(){
printf("Input by file?(\"y\"or\"n\")\n");
if(getchar()=='y') freopen("data.txt","r",stdin);
else printf("Put points you want.\nInput -1 -1 to finish\n");
init();
draw();
while(1){
round++;
judge();
draw();
Sleep(SLEEPTIME);
}
//fclose(stdout);
return 0;
}
以及配套的随机数据生成器
#include<cstdio>
#include<ctime>
#include<cstdlib>
int main(){
freopen("data.txt","w",stdout);
srand(time(0));
int amount=rand()%50+10;
for(int i=1;i<=amount;i++){
printf("%d %d\n",rand()%9+1,rand()%9+1);
}
printf("-1 -1\n");
fclose(stdout);
return 0;
}
So,当个创世神吧!