有穷状态机的形式定义
有穷状态机是一个五元组 (Q,Σ,δ,q0,F),其中:
Q是一个有穷集合,称为状态集。
Σ是一个有穷集合,称为字母表。
δ: Q xΣQ称为状态转移函数。
q0 是初始状态。
F 是接受状态集。
教科书上是这样定义有穷自动机的,这个形式定义精确的描述了有穷状态机的含义。但是大部分人(包括我自己)第一次看到它时,反复的读上几遍,仍然不知道它在说什么。幸好通过一些实例,我们可以很容易明白有穷状态机的原理。
自动门是一个典型的有穷状态机:
它有“开”和“关”两种状态,这就是它的状态集,也就是上面所说的Q。
人可以从自动门进来或出去,当人进来或出去的时候,自动门会自动打开,如果在规定的时间内没有人进出,自动门会自动关上。人的进来、出去和超时三个 事件是自动门的字母表,也就是上面所说的Σ。而自动门在当前状态下,对事件的响应,会引起状态的变化,这就是状态转换函数,也就是上面所说的δ。
自动门刚安装好的时候,我们可以认为它是关上的,所以关闭状态是自动门的初始状态。
在理想情况下,自动门会一直运行,所以它没有接受状态,接受状态集F是空集。
有穷状态机的形式定义很精确,文字描述比较通俗,而图形表示则比较直观。通用建模语言(UML)里的状态图是状态机的常用图形表示方法。简单的状态 图包括一些状态,用圆角方框表示,里面有状态的名称。状态之间的转换,用箭头表示,上面可以加转换条件。自动门的状态机可以用下图表示:
有穷状态机很简单,在生活中可以找出很多这样的例子。但是教科书里讲得太复杂了,一会儿证明确定性有穷状态机和非确定性有穷状态机的等价性,一会儿 证明正则表达式的正则运算是封闭的,一会儿又来个泵引理。花了很长时间,我才明白这些原理,但两年之后,我又把它们忘得一干二净。
主要原因是工作中没有机会运用它们,这些理论的证明于编程没有太大用处,不过状态机本身却是文本处理利器,由于程序员在很多场合下都是在与文本数据 打交道,所以状态机是程序员必备的工具之一。
统计一篇英文文章里的单词个数。
有多种方法可以解这道题,这里我们选择用有穷状态机来解,做法如下:
先把这篇英文文章读入到一个缓冲区里,让一个指针从缓冲区的头部一直移到缓冲区的尾部,
指针会处于两种状态:“单词内”或“单词外”,加上后面提到的初始状态和接受状态,就
是有穷状态机的状态集。缓冲区中的字符集合就是有穷状态机的字母表。
如果当前状态为“单词内”,移到指针时,指针指向的字符是非单词字符(如标点和空格),
那状态会从“单词内”转换到“单词外”。如果当前状态为“单 词外”, 移到指针时,指
针指向的字符是单词字符(如字母),那状态会从“单词外”转换到“单词内”。这些转换规
则就是状态转换函数。
指针指向缓冲区的头部时是初始状态。
指针指向缓冲区的尾部时是接受状态。
每次当状态从“单词内”转换到“单词外”时,单词计数增加一。
这个有穷状态机的图形表示如下:
代码如下:
#include<iostream>
using namespace std;
bool Is_Word(char ch)
{
if((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
return true;
return false;
}
int Calc_Words(const char* text)
{
enum State
{
WORD_INIT, //初始状态
WORD_IN, //单词中
WORD_OUT //单词外
}state = WORD_INIT;
int count = 0;
const char *p = text;
for(p = text; *p != '\0'; p++)
{
switch(state)
{
case WORD_INIT:
{
if(Is_Word(*p))
{
state = WORD_IN;
}
else
{
state = WORD_OUT;
}
}
break;
case WORD_IN:
{
if(!Is_Word(*p))
{
count++;
state = WORD_OUT;
}
break;
}
case WORD_OUT:
{
if(Is_Word(*p))
{
state = WORD_IN;
}
break;
}
default:
break;
}
}
if(state == WORD_IN)
{
/*如果由单词内进入接受状态,增加单词计数*/
count++;
}
return count;
}
int main()
{
char *text = "Where there is a will,there is a way!All the things in their being are good for something!";
int count = 0;
count = Calc_Words(text);
cout<<"There are "<<count<<" words in the text totaly!"<<endl;
return 0;
}