状态机理论最初的发展在数字电路设计领域。而在软件设计领域,状态机设计的理论俨然已经自成一体。

状态机是软件编程中的一个重要概念,比这个概念更重要的是对它的灵活应用。在一个思路清晰而且高效的程序中,必然有状态机的身影浮现。比如说一个按键命令解析程序,就可以被看做状态机:本来在A状态下,触发一个按键后切换到了B状态,再触发另一个键后切换到C状态,或者返回到A状态。这就是最简单的按键状态机例子。实际的按键解析程序会比这更复杂些,但这不影响我们对状态机的认识。进一步看,击键动作本身也可以看做一个状态机。一个细小的击键动作包含了:释放、抖动、闭合、抖动和重新释放等状态。

1. 状态机的历史

纯属个人观点,如有错误请指正

状态机衍生于离散数学中的图论,然后针对于特定场景下提出的延伸性概念。

图论最开始研究的是现实中的事物之间的关系。

比如图论里面经典的“七桥问题”,是有具体的事物:4个岛屿,7座桥梁。



随着科技的进步,发现一些新的事情无法用“事物”来表述。

而计算机程序的状态机则更加抽象了“事物”的概念,“状态”即“事物”。“状态”可以对应独立事物的状态,如人的状态:开心,悲伤,严肃,正常等;也可以对应多种事物间的关系,如我爱你,你爱他,他爱我。

我们也把多种事物的状态抽象成一个独立的整体,比如“三角恋”,可以当做一个独立的整体来研究。

其实很多时候我们延伸了状态的概念,比如数据,或者关系

2.概念

无限状态机属于理论上的一个模型,比如人脑,对于目前人类来说就属于无限状态机。

现在的人工智能AI,理论上是要实现的就应该是无限状态机。可以在任何情况下,做出合理的抉择。

而目前人工智能无法“打败”人类,也是因为状态是可以被枚举的。

不可预测是无限状态机么?

我们主要来研究有限状态机:状态可以被枚举的状态机。先来统一几个概念:

  • 输入:有可能触发状态改变的 数据、事件等;每个节点都必须至少有一个输入;
  • 转换:从一种状态转移到另一种状态之间的过程;“状态”被转换以后,可能是一种新态,也可能不变;
  • 状态:从某种维度建立的,描述当前系统、事物等的数据;
  • 接受状态(终态):被用户接受的状态;
  • 非接受状态:中间状态,不被用户接受,但现实存在的状态;

我们要说明的是DFA,即确定性有穷状态自动机:在输入一个状态时,只得到一个固定的状态。

3. 一个简单的DFA

人抽烟生病:

  • 正常状态A

  • 难受状态B

  • 住院状态C

  • h:普通抽一支,没事儿,还是A;

  • i: 抽多了,开始不舒服B;

  • j:检查出来生病了,肺部损伤住院C;

  • k:赶紧拿掉药,猛吃;

  • l:开始好转,回到默认状态;

    此时回到了A(正常)状态;

下图的状态演示图里面,表明这个烟民没有戒烟成功,还是在向C状态转移。

其中:

输入: h ,i ,j,k,l

转换: 箭头连线

状态:A,B,C

非接受态:A,B

接受态:C

我们用数学符号表示:

  • M = (Q,Σ, )
  • Q:有限状态集合(如 {A,B,C})
  • Σ: 有限输入集合(如{ h,i,j,k,l})
  • : 状态转换步骤(如 有向箭头)

为了更进一步区分:

  • M = (Q,Σ, ,q0,F)
  • q0: 初始态(如A)
  • F: 接受态集合(如{C})

在状态机的图里面,我们约定:

  • 一个圆圈代表 非接受态
  • 两个圆圈代表 接受态
  • 箭头代表转换函数
  • 箭头字符表示输入的数据、事件

4.实现状态机

状态机主要实现三大要素,

输入,

转换,

状态。


  1. /*
  2. M = (Q,Σ, ,q0,F)
  3. Q(别名State,简写 S): 有限状态集合(如 {A,B,C})
  4. Σ: 有限输入集合(如{ h,i,j,k,l}),外部输入
  5. (别名 Input,简写I) : 状态转换步骤(如 有向箭头)
  6. q0: 初始态(如A)
  7. F: 接受态集合(如{C})
  8. !!!!通过输入驱动的状态机!!!
  9. */
  10. export interface DFATransition<S> {
  11. /** 前置态 */
  12. from: S;
  13. /** 目标态 */
  14. to: S;
  15. /** 目标状态的执行函数 */
  16. toFunc: Function;
  17. }
  18. export class DFA<S, I extends string, T extends DFATransition<S>> {
  19. /**
  20. * 初始状态
  21. */
  22. private startState: S = null;
  23. /**
  24. * 当前状态
  25. */
  26. private _curState: S = null;
  27. /**
  28. * 接受态(最终态)
  29. */
  30. private acceptState: S[] = [];
  31. /**
  32. * 状态切换函数
  33. */
  34. private transMap: Map<I, T[]> = new Map();
  35. /**
  36. * 所有状态集合S
  37. */
  38. private stateSet: Set<S> = new Set();
  39. /**
  40. * 所有输入集合Σ
  41. */
  42. private inputSet: Set<I> = new Set();
  43. constructor(state: S, acceptState: S[]) {
  44. this.startState = state;
  45. this.acceptState = acceptState;
  46. this.resetStart();
  47. }
  48. get curState(): S {
  49. return this._curState;
  50. }
  51. set curState(s: S) {
  52. console.log('[DFA]DFA设置到状态:', s);
  53. this._curState = s;
  54. }
  55. /**
  56. * 恢复初始状态
  57. */
  58. public resetStart(): void {
  59. this.curState = this.startState;
  60. }
  61. /**
  62. * 创建状态机执行结构;
  63. * 如果已经存在当前结构,则覆盖:
  64. * 比如,从 a->b已经存在了,这次再次添加,则a->b被覆盖;
  65. * @param from 开始态
  66. * @param to 目标态
  67. * @param input 转换条件、事件
  68. * @param toFunc 目标态对应的执行器
  69. * @returns
  70. */
  71. public add = (from: S, to: S, input: I, toFunc: Function): DFA<S, I, T> => {
  72. // FIXME 不用as T会报错
  73. let newT = { from, to, toFunc } as T;
  74. let arr: T[] = this.transMap.get(input);
  75. if (!arr) {
  76. arr = [];
  77. this.transMap.set(input, arr);
  78. }
  79. // HACK 效率不高,看着好看,[]也会被执行一遍
  80. let hasCover = false;
  81. for (let i = 0, len = arr.length; i < len; i++) {
  82. let t = arr[i];
  83. if (t.from === from) {
  84. // 为什么没有判断 t.to == to? 因为转移的条件是 状态+输入,目标状态是确定的;如果加入to,可能能 输入i+状态s -》可能会对应不同的结果;
  85. // 如果存在当前转移
  86. arr[i] = newT;
  87. hasCover = true;
  88. }
  89. }
  90. if (!hasCover) {
  91. arr.push(newT);
  92. }
  93. this.stateSet.add(from).add(to);
  94. this.inputSet.add(input);
  95. return this;
  96. }
  97. /**
  98. * 事件输入
  99. * @param i
  100. */
  101. public input = (i: I): boolean => {
  102. let arr: T[] = this.transMap.get(i);
  103. let t: T = null;
  104. if (arr && arr.length) {
  105. // 找到当前转移
  106. arr.some((item: T) => {
  107. if (item.from === this.curState) {
  108. t = item;
  109. console.log('[DFA]DFA转移,起始态:' + t.from + '终止态:' + t.to);
  110. this.curState = item.to;
  111. item.toFunc && item.toFunc();
  112. return true;
  113. }
  114. });
  115. }
  116. if (!t) {
  117. console.log('[DFA]DFA无法响应输入事件,无法处理当前转移类型:' + i, '当前状态:' + this.curState);
  118. return false;
  119. } else {
  120. return true;
  121. }
  122. }
  123. /**
  124. * 当前状态机是否存在对应的状态
  125. * @param s 被判断的状态
  126. * @returns
  127. */
  128. public hasState(s: S): boolean {
  129. return this.stateSet.has(s);
  130. }
  131. public getStateLen(): number {
  132. return this.stateSet.size;
  133. }
  134. /**
  135. *
  136. * @returns 是否是初始态
  137. */
  138. public isStartState = (): boolean => this.curState === this.startState
  139. /**
  140. *
  141. * @returns 当前状态是否被接受
  142. */
  143. public canAccept = (): boolean => this.acceptState.includes(this.curState)
  144. }

4. 状态机与状态模式

都有“状态”。

状态机是建模方式,是一种模型;

状态模式是基于状态改变行为的设计模式,表示状态的行为。

5. 应用场景

在项目中尝试用状态机制作触摸。

触摸包含 :开始触摸,touchBegin;触摸移动,touchMove; 触摸结束,touchEnd

其中,点击事件是 touchBegin+touchEnd

实现以后,发现代码非常难维护;比如当前物件既有拖动,又有点击的时候,很难去清晰的进行状态转移。会新增一些hack写法。

6.总结

个人认为状态机思想是惊为天人的,在一些领域有重要意义。而在前端交互上,用状态机实现一些东西,容易出现各种问题:

如:设置“伪态”;他人难以维护;新增状态时需要非常谨慎。

大多数场景下,我们不一定要用他,通过别的方式也可以实现。

内心一定要有它!!!

参考:

自动机,状态机,有限自动机,有限状态机,有限状态自动机,非确定下有限状态自动,确定性有限状态自动机的区别于联系

http://www.voidcn.com/article/p-bgmycagl-de.html

谈谈状态机

https://zhuanlan.zhihu.com/p/28142401

编译原理笔记3:有限自动机

https://www.jianshu.com/p/afad52d4c5d4

证明与计算(7): 有限状态机(Finite State Machine)

https://www.cnblogs.com/math/p/fsm.html

浅谈状态机原理及其应用

https://blog.csdn.net/zhuqiuhui/article/details/102533721?utm_medium=distribute.pc_relevant_download.none-task-blog-baidujs-2.nonecase&depth_1-utm_source=distribute.pc_relevant_download.none-task-blog-baidujs-2.nonecase

【翻译】游戏设计模式之状态机

https://zhuanlan.zhihu.com/p/74984237

游戏状态机实现介绍

https://blog.csdn.net/yonshi/article/details/40082021

技术系列之“状态机”

https://kb.cnblogs.com/page/528966/

状态机思路在程序设计中的应用

https://kb.cnblogs.com/page/528971/

《有限状态机在开放式数控系统中的应用》

简聊DFA(确定性有限状态自动机)的更多相关文章

  1. DFA确定有限状态自动机

    DFA 在计算理论中,确定有限状态自动机或确定有限自动机(英语:deterministic finite automaton, DFA)是一个能实现状态转移的自动机.对于一个给定的属于该自动机的状态和 ...

  2. K:有限状态自动机

      有限状态自动机是一种特殊的状态机.它表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型.有限状态自动机分为两种,一种是 确定有限状态自动机(DFA) ,一种是 非确定有限状态自动机(NF ...

  3. Trie 前缀树或字典树 确定有限状态自动机

    https://zh.wikipedia.org/wiki/Trie 应用 trie树常用于搜索提示.如当输入一个网址,可以自动搜索出可能的选择.当没有完全匹配的搜索结果,可以返回前缀最相似的可能.[ ...

  4. 用C语言实现有限状态自动机FSM

    摘要:状态机模式是一种行为模式,在<设计模式>这本书中对其有详细的描述,通过多态实现不同状态的调转行为的确是一种很好的方法,只可惜在嵌入式环境下,有时只能写纯C代码,并且还需要考虑代码的重 ...

  5. 非确定有限状态自动机的构建(二)——将CharVal转换为NFA

    保留版权,转载注明出处:潘军彪的个人博客(http://blog.csdn.net/panjunbiao/article/details/9378933) 将上下文无关文法读入内存之后,可以将它转换成 ...

  6. 非确定有限状态自动机的构建(一)——NFA的定义和实现

    保留版权,转载需注明出处(http://blog.csdn.net/panjunbiao). 非确定有限状态自动机(Nondeterministic Finite Automata,NFA)由以下元素 ...

  7. 【Codeforces 506E】Mr.Kitayuta’s Gift&&【BZOJ 4214】黄昏下的礼物 dp转有限状态自动机+矩阵乘法优化

    神题……胡乱讲述一下思维过程……首先,读懂题.然后,转化问题为构造一个长度为|T|+n的字符串,使其内含有T这个子序列.之后,想到一个简单的dp.由于是回文串,我们就增量构造半个回文串,设f(i,j, ...

  8. <轻量算法>根据核密度估计检测波峰算法 ---基于有限状态自动机和递归实现

    原创博客,转载请联系博主! 希望我思考问题的思路,也可以给大家一些启发或者反思! 问题背景: 现在我们的手上有一组没有明确规律,但是分布有明显聚簇现象的样本点,如下图所示: 图中数据集是显然是个3维的 ...

  9. 51NOD 1292 1277(KMP算法,字符串中的有限状态自动机)

    在前两天的CCPC网络赛中...被一发KMP题卡了住了...遂决定,哪里跌倒就在哪里爬起来...把个KMP恶补一发,连带着把AC自动机什么的也整上. 首先,介绍设定:KMP算法计划解决的基本问题是,两 ...

随机推荐

  1. Java关键字(八)——synchronized

    synchronized 这个关键字,我相信对于并发编程有一定了解的人,一定会特别熟悉,对于一些可能在多线程环境下可能会有并发问题的代码,或者方法,直接加上synchronized,问题就搞定了. 但 ...

  2. ES6新增数组的一些思考和使用

    ES6数组的新增 伪数组转换为数组的两种方法 Array.from()把一个伪数组转换为一个真正的数组 伪数组:有下标和length,但是不能使用数组方法 let lis = document.que ...

  3. [bug] IDEA Maven 项目 Module 不加粗,无法编译

    参考 https://blog.csdn.net/qq_42479920/article/details/102859244

  4. unrar命令解压rar unrar e XXX.rar (验证通过20200511)

    unrar命令解压rar 一个从入门到放弃再到改行的工程师 2018-05-02 17:53:04 3916 收藏展开压缩tar -cvf jpg.tar *.jpg //将目录里所有jpg文件打包成 ...

  5. 目录和文件 按创建时间排序du -h --time --max-depth=1 . |sort -r -t $'\t' -k 2 Linux查看文件夹大小,并按文件夹创建时间排序

    目录和文件 按创建时间排序 # du -h --time --max-depth=1 . |sort -r -t $'\t' -k 230M 2020-04-01 14:54 .28K 2020-04 ...

  6. 如何在我的EC2实例状态更改时获取自定义电子邮件通知

    具体详情,请参见: https://amazonaws-china.com/cn/premiumsupport/knowledge-center/ec2-email-instance-state-ch ...

  7. python3+unittest参考

    Python3+Selenium+unittest自动化UI测试框架:https://www.cnblogs.com/G2Bent/p/8376001.html unittest --- 单元测试框架 ...

  8. python3 使用random函数批量产生注册邮箱

    '''你是一个高级测试工程师,现在要做性能测试,需要你写一个函数,批量生成一些注册使用的账号. 1.产生的账号是以@163.com结尾,长度由用户输,产生多少条也由用户输入,2.用户名不能重复,用户名 ...

  9. Day029 JDK8中新日期和时间API (二)

    # JDK8中新日期和时间API (二) Instant介绍 Instant:时间线上的一个瞬时点. 这可能被用来记录应用程序中的事件时间 戳. 在处理时间和日期的时候,我们通常会想到年,月,日,时, ...

  10. 深入 Go 语言 defer 实现原理

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客: https://www.luozhiyun.com/archives/523 本文使用的go的源码 1.15.7 介绍 defer 执行规 ...