计算理论导引实验一:NFA对字符串的识别

计算理论导引实验一:NFA对字符串的识别

  • 实验描述
  • 问题解决
    • 状态类
    • 状态转移关系类
    • 键盘输入及逻辑处理类
  • 测试运行
  • 代码下载及说明
    • 下载
    • 说明

课程结束了,但是在对这门课的理解上,可能还有不足之处。现在有时间便将当时的实验内容及使用的解决方法记录下来。

实验描述

实验中要求理解NFA工作原理设计数据结构或类,编写代码实现下图中的NFA,通过某些字符串作为输入,通过代码运行判断NFA能否正确接受或拒绝这些字符串,并将得到的结果进行输出。

计算理论导引实验一:NFA对字符串的识别_第1张图片

状态转移函数关系如下表所示,在接下来的分析编程过程中,使用#表示ε字符,用NO表示状态转移为空集的状态。

0 1 空【用#号表示】
q1 q1 q1,q2 NO
q2 q3 NO q3
q3 NO q4 NO
q4 q4 q4 NO

起始项:q1
终止接受状态:q4
程序中对每个状态都进行转移,最后判断结束时,最后状态为q4则接受,状态为其他则拒绝

问题解决

在针对上面的NFA及实验要求进行分析之后,可将整个过程抽象成三个类,分别为状态类,状态转移类,键盘输入及逻辑处理类。

状态类

该类中主要用于表示NFA状态机的初始状态及接受状态,该类的代码如下:

public class State {
	private String beginState;//状态机的初始状态
	private String endState;//状态机的接收终止状态
	public String getBeginState() {
		return beginState;
	}
	public void setBeginState(String beginState) {
		this.beginState = beginState;
	}
	public String getEndState() {
		return endState;
	}
	public void setEndState(String endState) {
		this.endState = endState;
	}
	public State(String beginState, String endState) {
		super();
		this.beginState = beginState;
		this.endState = endState;
	}
	public State() {
		super();
	}	
}

状态转移关系类

该类主要用于表示状态的转移关系,代码如下:

public class Old2New {
	private String oldState;
	private char trans;
	private String newState;
	public String getOldState() {
		return oldState;
	}
	public void setOldState(String oldState) {
		this.oldState = oldState;
	}
	public char getTrans() {
		return trans;
	}
	public void setTrans(char trans) {
		this.trans = trans;
	}
	public String getNewState() {
		return newState;
	}
	public void setNewState(String newState) {
		this.newState = newState;
	}
	public Old2New(String oldState, char trans, String newState) {
		super();
		this.oldState = oldState;
		this.trans = trans;
		this.newState = newState;
	}
}

键盘输入及逻辑处理类

该类是用来进行一系列的初始化及对字符状态集进行操作处理的类,主要包括以下几项:

  1. 初始化待识别字符串
    通过键盘输入字符串,将字符串保存到字符数组中。
  2. 初始化状态对象
    将状态对象实例化,确定状态机的初始状态和接受状态。
    public static ArrayList<Old2New> initRelation() {
    		//将拒绝状态用NO表示,将空串用#表示
    		ArrayList<Old2New> list = new ArrayList<Old2New>();
    		list.add(new Old2New("q1",'0',"q1"));
    		list.add(new Old2New("q1",'1',"q1"));
    		list.add(new Old2New("q1",'1',"q2"));
    		list.add(new Old2New("q1",'#',"NO"));
    		list.add(new Old2New("q2",'0',"q3"));
    		list.add(new Old2New("q2",'1',"NO"));
    		list.add(new Old2New("q2",'#',"q3"));
    		list.add(new Old2New("q3",'0',"NO"));
    		list.add(new Old2New("q3",'1',"q4"));
    		list.add(new Old2New("q3",'#',"NO"));
    		list.add(new Old2New("q4",'0',"q4"));
    		list.add(new Old2New("q4",'1',"q4"));
    		list.add(new Old2New("q4",'#',"NO"));
    		return list;
    	}
    
  3. 初始化状态转移关系集合
    将状态机的每个状态转移关系保存到集合中。每一个状态转移关系都是状态转移关系类的一个实例化对象,包含了当前状态,当前识别的字符以及将要转移到的新的状态。
  4. 从前到后依次遍历读取字符
    对于每一个字符都进行下列操作
    a)克隆集合,并在克隆前清空克隆的hstmp集合,保证hstmp集合中仅含有当前状态集合中的元素
    b)用克隆的hstmp集合进行比较,遍历tmp集合,当前状态下待识别的字符对应有非空集合的状态时,添加到hs集合中.
    //遍历集合
    for (Iterator iterator = hsTmp.iterator(); iterator.hasNext();) {
    	//获取集合中的状态
    	String strState = (String) iterator.next();
    	System.out.println("当前状态:"+strState);
    	for (Old2New stateTr : list)
    	{//遍历所有的状态,由于可能存在多个新的状态,因此需要将这多个新的状态全部记录,对于每个新的状态单独	
    	    if(strState.equals(stateTr.getOldState())&&(cin[i]==stateTr.getTrans())) {
    	    	if (!"NO".equals(stateTr.getNewState())) {//如果状态的转移不为空,将新状态添加到集
    		    	hs.add(stateTr.getNewState());//将新的状态添加到集合,先将其记录下来
    		    	System.out.println("当前状态:"+stateTr.getOldState()+" 识别字符:"+stateTr.getTrans()+"转移到新状态:"+stateTr.getNewState()+"添加状态:"+stateTr.getNewState());
    			}
    	    }				
    	}	
    }
    
    c)对hs集合进行遍历,有空转移时,将新的状态添加到hs集合中,得到识别当前字符时的空闭包。
    //单独处理,用来得到空闭包
    //遍历集合,得到空闭包
    for (Iterator iterator = hs.iterator(); iterator.hasNext();) {
    	//获取集合中的状态
    	String strState = (String) iterator.next();
    	System.out.println("单独识别空字符,当前状态:"+strState);
    	for (Old2New stateTr : list)
    	{//遍历所有的状态,由于可能存在多个新的状态,因此需要将这多个新的状态全部记录,对于每个新的状态单独	
    	    if (strState.equals(stateTr.getOldState())&&('#'==stateTr.getTrans())) {
    	    	if (!"NO".equals(stateTr.getNewState())) {//如果状态的转移不为空,将新状态添加到集
    		    	hs.add(stateTr.getNewState());//将新的状态添加到集合,先将其记录下来
    		    	System.out.println("添加状态:"+stateTr.getNewState());
    			}
    		}				
    	}	
    }
    

所有待识别字符识别处理完成之后,对于最终的ε闭包集合,判断是否包含接受状态,包含则字符串被状态机接受,否则被拒绝。

测试运行

例如输入为11时,从NFA可以看出输入为11时,也会被NFA接受。运行代码效果如下:

请输入仅含[0,1](中间也可输入用#表示的空字符,#可写可不写)待识别的字符串:
11
--------------------------------------------
识别第1个字符1
当前状态:q1
当前状态:q1 识别字符:1转移到新状态:q1添加状态:q1
当前状态:q1 识别字符:1转移到新状态:q2添加状态:q2
单独识别空字符,当前状态:q1
单独识别空字符,当前状态:q2
添加状态:q3
识别当前字符:1,得到的空闭包为{q1,q2,q3,}
--------------------------------------------
--------------------------------------------
识别第2个字符1
当前状态:q1
当前状态:q1 识别字符:1转移到新状态:q1添加状态:q1
当前状态:q1 识别字符:1转移到新状态:q2添加状态:q2
当前状态:q2
当前状态:q3
当前状态:q3 识别字符:1转移到新状态:q4添加状态:q4
单独识别空字符,当前状态:q1
单独识别空字符,当前状态:q2
添加状态:q3
单独识别空字符,当前状态:q3
单独识别空字符,当前状态:q4
识别当前字符:1,得到的空闭包为{q1,q2,q3,q4,}
--------------------------------------------
ε闭包集合中包含接受状态 q4,该字符串被状态机接收
------------------END-------------------

Process finished with exit code 0

代码下载及说明

下载

目前代码已传到github上,可以 点我下载

说明

目前代码可能只是针对这一NFA的解决实现,例如接受状态集合在该实验中只有一个状态,因此对于其他NFA可能需要进行调整,以及需要根据实际的NFA进行状态转移关系集合的初始化。

你可能感兴趣的:(计算理论导引)