元胞自动机实现多数分类算法
2018-02-10 20:17
120 查看
元胞自动机(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:using System; using System.Drawing; using System.Windows.Forms; namespace Skyiv.CellularAutomaton.MajorityClassification { sealed class MainForm : Form { static readonly int sizeCellular = 3; static readonly int nCellular = 201; static readonly int lines = nCellular; static readonly Pen pen = new Pen(Color.Black, sizeCellular); static readonly string strRuler = "0000010100000110000101011000011100000111000001000001010101" + "0101110110010001110111000001010000000101111101111111111011" + "011101111111"; static readonly bool[] ruler = new bool[strRuler.Length]; Graphics gc; MainForm() { Text = "Majority Classification"; BackColor = Color.White; ClientSize = new Size(nCellular * sizeCellular + 1, lines * sizeCellular + 1 + 32); for (var i = 0; i < ruler.Length; i++) ruler[i] = strRuler[i] == '1'; } protected override void OnPaint(PaintEventArgs e) { gc = e.Graphics; DrawGrid(true); var cellulars = GetInitCellulars(); DrawCellulars(cellulars, 0); DisplayMessage(cellulars); for (var i = 1; i < lines; i++) { StepIt(cellulars); DrawCellulars(cellulars, i); } base.OnPaint(e); } void StepIt(bool[] cellulars) { var buf = new bool[cellulars.Length]; for (var i = 0; i < nCellular; i++) buf[i] = ruler[GetValue(cellulars, i)]; Array.Copy(buf, cellulars, cellulars.Length); } int GetValue(bool[] cellulars, int idx) { var n = 0; idx = (idx + 3) % nCellular; for (var i = 0; i < 7; i++) if (cellulars[(idx - i + nCellular) % nCellular]) n += 1 << i; return n; } void DrawCellulars(bool[] cellulars, int line) { for (var i = 0; i < cellulars.Length; i++) if (cellulars [i]) Set(i, line); } void DisplayMessage(bool[] cellulars) { var blacks = 0; foreach (var cellular in cellulars) if (cellular) blacks++; Out("Black:{0} White:{1}", blacks, cellulars.Length - blacks); } void Out(string fmt, params object[] args) { gc.DrawString(string.Format(fmt, args), new Font("Courier New", 10), Brushes.Blue, new Point(5, lines * sizeCellular + 9)); } bool[] GetInitCellulars() { var rand = new Random(); var cellulars = new bool[nCellular]; for (var i = 0; i < cellulars.Length; i++) cellulars [i] = rand.Next() % 2 == 0; return cellulars; } void DrawGrid(bool onlyBorder) { var pen = new Pen(Color.Red, 1); var len = nCellular * sizeCellular; for (var i = onlyBorder ? lines : 0; i <= lines; i++) { var k = i * sizeCellular; gc.DrawLine(pen, 0, k, len, k); } len = lines * sizeCellular; for (var i = onlyBorder ? nCellular : 0; i <= nCellular; i++) { var k = i * sizeCellular; gc.DrawLine(pen, k, 0, k, len); } } void Set(int x, int y) { var y2 = y * sizeCellular + sizeCellular / 2; gc.DrawLine(pen, x * sizeCellular, y2, (x + 1) * sizeCellular, y2); } static void Main() { Application.Run(new MainForm()); } } }
简要分析
第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版
相关文章推荐
- 用元胞自动机实现多数分类算法
- 文本分类算法之--贝叶斯分类算法的实现Java版本
- bayes分类算法之java实现
- 第一篇:K-近邻分类算法原理分析与代码实现
- 【机器学习PAI实践七】文本分析算法实现新闻自动分类
- 决策树C4.5分类算法的C++实现
- 左右值无限分类实现算法
- 1.5 KNN算法学习——KNN算法分类模型的实现与分类准确度评估
- 机器学习算法(二)——决策树分类算法及R语言实现方法
- 分类算法--贝叶斯分类法(Maprdecue实现)<转>
- 转:无限级分类的简单算法实现及代码重点讲解
- 鸢尾花分类算法实现 java
- KNN-分类算法的实现
- 数据挖掘-决策树ID3分类算法的C++实现
- 无限分类左右值实现算法
- 基于左右值的无限级分类算法-oracle实现--03
- [Weka]在自己的算法中调用Weka实现文本分类的一个例子
- 分类算法-----朴素贝叶斯原理和python实现
- 【JAVA实现】K-近邻(KNN)分类算法
- 机器学习经典算法详解及Python实现--CART分类决策树、回归树和模型树