元胞自动机实现多数分类算法
元胞自动机(Cellular automaton)
元胞自动机是由元胞组成的网格,每个元胞都根据邻域的状态来选择开或关。所有的元胞都遵循同样的规则,也称为元胞的更新规则,规则根据各元胞邻域的当前状态决定元胞的下一步状态。同自然界的复杂系统一样,元胞自动机也是由大量简单个体(元胞)组成,不存在中央控制,每个个体都只与少量其他个体交互。而且元胞自动机也能表现出非常复杂的行为,它们的行为很难甚至不可能通过其更新规则来预测。元胞自动机有很多种类型,著名的“生命游戏”也是元胞自动机的一种。
初等元胞自动机(Elementary cellular automaton)
初等元胞自动机是一维两状态的元胞自动机,每个元胞仅与两个相邻元胞相连。元胞自动机的时空图表现了元胞自动机的立体构型随时间的变化,最顶上一行是一维元胞自动机的初始状态设置,下面跟着的依次是每一步更新后的状态。
执行“多数分类(Majority classification)”任务的元胞自动机
该元胞自动机要能区分初始状态中是开状态还是关状态占多数。如果是开状态占多数,最后所有元胞就应当都变成开状态。同样,如果是关状态占多数,最后所有元胞就应该都变成关状态。多数分类任务有点类似于选举,是在大家都只知道最近邻居政治观点下预测两个候选人谁会赢。
我们使用一维元胞自动机,每个元胞与相邻的6个元胞相连,这样元胞的邻域中就有7个元胞(包括自己)。一个合理的想法是:“元胞应当变成邻域中当前占多数的状态。”这就好象根据你自己和邻居的多数意见来预测哪个候选人会当选。然而,这个“局部多数投票”元胞自动机并不能完成任务。
我们使用的是梅拉妮·米歇尔(Melanie Mitchell)在《复杂》(Complexity: A Guided Tour)一书第203页给出的规则:
0000010100000110000101011000011100000111000001000001010101
0101110110010001110111000001010000000101111101111111111011
011101111111
第1位是邻域全为0时中间元胞的更新状态,第2位是邻域为0000001时中间元胞的更新状态,依次往后。由于邻域状态有 27 = 128 种可能,因此该规则有128位。但是光看这些数位是看不出这个规则如何运作,也无法知道为何它进行多数分类时适应度很高。
实现该算法的 C# 程序
下面就是相应的 C# 源程序 MainForm.cs:
1 using System;
2 using System.Drawing;
3 using System.Windows.Forms;
4
5 namespace Skyiv.CellularAutomaton.MajorityClassification
6 {
7 sealed class MainForm : Form
8 {
9 static readonly int sizeCellular = 3;
10 static readonly int nCellular = 201;
11 static readonly int lines = nCellular;
12 static readonly Pen pen = new Pen(Color.Black, sizeCellular);
13 static readonly string strRuler =
14 "0000010100000110000101011000011100000111000001000001010101" +
15 "0101110110010001110111000001010000000101111101111111111011" +
16 "011101111111";
17 static readonly bool[] ruler = new bool[strRuler.Length];
18
19 Graphics gc;
20
21 MainForm()
22 {
23 Text = "Majority Classification";
24 BackColor = Color.White;
25 ClientSize = new Size(nCellular * sizeCellular + 1, lines * sizeCellular + 1 + 32);
26 for (var i = 0; i < ruler.Length; i++) ruler[i] = strRuler[i] == '1';
27 }
28
29 protected override void OnPaint(PaintEventArgs e)
30 {
31 gc = e.Graphics;
32 DrawGrid(true);
33 var cellulars = GetInitCellulars();
34 DrawCellulars(cellulars, 0);
35 DisplayMessage(cellulars);
36 for (var i = 1; i < lines; i++)
37 {
38 StepIt(cellulars);
39 DrawCellulars(cellulars, i);
40 }
41 base.OnPaint(e);
42 }
43
44 void StepIt(bool[] cellulars)
45 {
46 var buf = new bool[cellulars.Length];
47 for (var i = 0; i < nCellular; i++)
48 buf[i] = ruler[GetValue(cellulars, i)];
49 Array.Copy(buf, cellulars, cellulars.Length);
50 }
51
52 int GetValue(bool[] cellulars, int idx)
53 {
54 var n = 0;
55 idx = (idx + 3) % nCellular;
56 for (var i = 0; i < 7; i++)
57 if (cellulars[(idx - i + nCellular) % nCellular])
58 n += 1 << i;
59 return n;
60 }
61
62 void DrawCellulars(bool[] cellulars, int line)
63 {
64 for (var i = 0; i < cellulars.Length; i++)
65 if (cellulars [i])
66 Set(i, line);
67 }
68
69 void DisplayMessage(bool[] cellulars)
70 {
71 var blacks = 0;
72 foreach (var cellular in cellulars)
73 if (cellular)
74 blacks++;
75 Out("Black:{0} White:{1}", blacks, cellulars.Length - blacks);
76 }
77
78 void Out(string fmt, params object[] args)
79 {
80 gc.DrawString(string.Format(fmt, args), new Font("Courier New", 10),
81 Brushes.Blue, new Point(5, lines * sizeCellular + 9));
82 }
83
84 bool[] GetInitCellulars()
85 {
86 var rand = new Random();
87 var cellulars = new bool[nCellular];
88 for (var i = 0; i < cellulars.Length; i++)
89 cellulars [i] = rand.Next() % 2 == 0;
90 return cellulars;
91 }
92
93 void DrawGrid(bool onlyBorder)
94 {
95 var pen = new Pen(Color.Red, 1);
96 var len = nCellular * sizeCellular;
97 for (var i = onlyBorder ? lines : 0; i <= lines; i++)
98 {
99 var k = i * sizeCellular;
100 gc.DrawLine(pen, 0, k, len, k);
101 }
102 len = lines * sizeCellular;
103 for (var i = onlyBorder ? nCellular : 0; i <= nCellular; i++)
104 {
105 var k = i * sizeCellular;
106 gc.DrawLine(pen, k, 0, k, len);
107 }
108 }
109
110 void Set(int x, int y)
111 {
112 var y2 = y * sizeCellular + sizeCellular / 2;
113 gc.DrawLine(pen, x * sizeCellular, y2, (x + 1) * sizeCellular, y2);
114 }
115
116 static void Main()
117 {
118 Application.Run(new MainForm());
119 }
120 }
121 }
简要分析
- 第9行的静态只读变量 sizeCellular 表示每个元胞方格的边长,必须为奇数。
- 第10行的静态只读变量 nCellular 表示每行有多少个元胞,最好为奇数,以免在多数分类时出现平局的情况。
- 第11行的静态只读变量 lines 表示要迭代多少次。
- 第13行的静态只读变量 strRuler 表示迭代的规则。
- 主要工作在从第29行到42行的 OnPaint 方法中进行。
- 第84行到第91行的 GetInitCellulars 方法随机地初始化元胞的初始状态。
- 第36行到第40行的循环按照指定的规则进行迭代。
- 第44行到第50行的 StepIt 方法执行具体的迭代步骤。
- 第52行到第60行的 GetValue 方法计算元胞邻域的值。
运行结果
该程序几次典型的运行结果如下所示:
上图显示白色占优,结果正确。这种情况很常见。
上图显示黑色占优,结果正确。这种情况也很常见。
上图显示白色占优,结果错误。这种情况比较少见。
上图显示黑色占优,结果错误。这个结果错误在于最终的迭代结果不是全黒,而混入了少量的白色元胞。这个程序只迭代 200 步,其实只要再迭代几步后就可以得到正确的结果。这种情况非常的少见。
参考资料
- Wikipedia: Majority problem (cellular automaton)
- Wikipedia: Cellular automaton
- Wikipedia: Elementary cellular automaton
- 《复杂》,梅拉妮·米歇尔著,唐璐译,湖南科学技术出版社,2011年6月第1版
- 三、请求库之requests模块
- 升级 微信技术输出 时尚新门店尝鲜刷脸支付
- WSP Global品牌升级 启用3声母域名
- 一、爬虫基本原理
- python 中__setattr__, __getattr__,__getattribute__, __call__使用方法
- 量子技术与人工智能:同时进化的双生子
- TCP协议三次握手与四次挥手通俗解析
- Silverlight/aspx/ajax/mvc的UI自动化测试
- Office Open XML学习(1)-创建excel文档,并向单元格中插入字符串
- PyMC3和Theano代码构建贝叶斯深度网络,61页PPT探索贝叶斯深度学习以及实现
- 男程序员是不是都不会和女生表达交流?程序员的回答歪了
- Silverlight Telerik控件学习:主题Theme切换
- Silverlight自定义类库实现应用程序缓存
- Silverlight Telerik控件学习:TreeView数据绑定并初始化选中状态、PanelBar的Accordion效果、TabPanel、Frame基本使用
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- PHP getName()函数讲解
- Laravel框架集成UEditor编辑器的方法图文与实例详解
- PHP+redis实现的购物车单例类示例
- ThinkPHP3.2.3框架邮件发送功能图文实例详解
- PHP simplexml_load_file()函数讲解
- Python下划线5种含义代码实例解析
- PHP getDocNamespaces()函数讲解
- Django实现内容缓存实例方法
- Tensorflow–取tensorf指定列的操作方式
- spring-boot-route(一)Controller接收参数的几种方式
- python中 _、__、__xx__()区别及使用场景
- 浅谈TensorFlow中读取图像数据的三种方式
- python 最简单的实现适配器设计模式的示例
- spring-boot-route(二):读取配置文件的几种方式
- 关于tensorflow softmax函数用法解析