书名:代码本色:用编程模拟自然系统
作者:Daniel Shiffman
译者:周晗彬
ISBN:978-7-115-36947-5
第7章目录
先掌握用Processing Sketch创建和可视化Wolfram CA模型的方法。
7.3 如何编写初等细胞自动机
int[] cells = {1,0,1,0,0,0,0,1,0,1,1,1,0,0,0,1,1,1,0,0};
为了绘制这个数组,我们可以根据元素的状态填充对应的颜色。
for (int i = 0; i < cells.length; i++) { 遍历每个细胞
if (cell[i] == 0) fill(255);
else fill(0); 根据状态(0或1)填充细胞颜色
stroke(0);
rect(i*50,0,50,50);
}
现在,我们用数组描述一次迭代(只考虑“当前”的迭代)的细胞状态,下面还要引入计算下一次迭代的机制。我们先用伪代码表示当前要做的事。
对数组中的每个细胞:
for (int i = 0; i < cells.length; i++) { 对数组中的每个细胞 ......
int left = cell[i-1]; 获取邻居的状态
int middle = cell[i];
int right = cell[i+1];
int newstate = rules(left,middle,right); 根据先前设定的规则查询新的状态
cell[i] = newstate; 把细胞的状态设为新值
}
现在有3种解决方案可供选择。
为了让代码尽可能地易读易理解,我们采用第一种方案:直接略过边界细胞,让它们的状态保持常量。这种方案的实现很简单,只需要让循环从下标1开始,并提前一个元素结束:
for (int i = 1; i < cells.length-1; i++) { 忽略第一个和最后一个细胞
int left = cell[i-1];
int middle = cell[i];
int right = cell[i+1];
int newstate = rules(left,middle,right);
cell[i] = newstate;
}
上面代码看起来并没有错误:一旦我们得到新状态,确实需要将它赋给当前细胞。但在下一次迭代中,你会发现一个严重的漏洞。会覆盖当前代的状态。
这个问题的解决方案是:使用两个数组,一个数组用于保存当前代的状态,另一个数组用于保存下一代的状态。
int [] newcells = new int[cells.length]; 用另一个数组保存下一代状态
for (int i = 1; i < cells.length-1; i++) {
int left = cell[i-1]; 从当前数组获取细胞状态
int middle = cell[i];
int right = cell[i+1];
int newstate = rules(left,middle,right);
newcells[i] = newstate; 在新数组中保存新状态
}
处理完数组的每个元素后,我们就可以丢弃旧的数组,让它的值等于新数组。
cells = newcells; 新一代状态变成了当前代状态
int[] ruleset = {0,1,0,1,1,0,1,0};
然后:
if (a == 1 && b == 1 && c == 1) return ruleset[0];
int rules (int a, int b, int c) {
if (a==1 && b == 1 && c == 1) return ruleset[0];
else if (a == 1 && b == 1 && c == 0) return ruleset[1];
else if (a == 1 && b == 0 && c == 1) return ruleset[2];
else if (a == 1 && b == 0 && c == 0) return ruleset[3];
else if (a == 0 && b == 1 && c == 1) return ruleset[4];
else if (a == 0 && b == 1 && c == 0) return ruleset[5];
else if (a == 0 && b == 0 && c == 1) return ruleset[6];
else if (a == 0 && b == 0 && c == 0) return ruleset[7];
return 0; 为了让函数的定义合法,必须加上这个返回值,虽然我们知道不可能出现不符合这8种情况}
另一种解决方案有点难以理解,就是把邻居状态的组合(3位二进制数)转换成一个普通整数,并把该值作为规则数组的下标。实现方式如下:
int rules (int a, int b, int c) {
String s = "" + a + b + c; 将3位转化为字符串
int index = Integer.parseInt(s,2); 第二个参数“2”告诉parseInt()函数要把s当成二进制数
return ruleset[index];
}
class CA {
int[] cells; 我们需要两个数组,一个用来存放细胞,另一个用来存放规则
int[] ruleset;
CA() {
cells = new int[width];
ruleset = {0,1,0,1,1,0,1,0}; 随意选取规则90
for (int i = 0; i < cells.length; i++) {
cells[i] = 0;
}
cells[cells.length/2] = 1; 除了中间的细胞以状态1开始,其余所有细胞都从状态0开始
}
void generate() {
int[] nextgen = new int[cells.length]; 计算下一代状态
for (int i = 1; i < cells.length-1; i++) {
int left = cells[i-1];
int me = cells[i];
int right = cells[i+1];
nextgen[i] = rules(left, me, right);
}
cells = nextgen;
}
int rules (int a, int b, int c) { 在规则集中查询新状态
String s = "" + a + b + c;
int index = Integer.parseInt(s,2);
return ruleset[index];
}
}