计算理论导引实验二:构造下推自动机


关于下推自动机的概念,可以从书上或者搜索一下了解的很详细,这里就不再多说,直接转入实验内容。

实验描述

实验的具体要求如下:
PDA实验描述

形式化定义

根据实验描述,可以将下推自动机进行如下设计,得到此PDA的形式化定义:
1. 状态集为{q1,q2,q3,q4,q5,q6,q7}
2. 输入字母表为{a,b,c}
3. 栈字母表为{b,c,$}
4. 起始状态为q1
5. 接受状态集为{q7}
6. 转移函数如下表所示,表中的空白项表示空集
状态转移函数关系表

状态图描述

PS:状态图还是当时实验时画的图片,现在就没有重新再画。
状态图

使用draw.io对状态图进行重画
在这里插入图片描述

问题解决

在对PDA进行了解之后,实现过程中需要借助到栈,同时考虑到本实验和实验一中的结构上有些类似,因此在实现过程中,便在实验一的基础上进行了调整。将实现过程中,分为了状态转移类和键盘录入及逻辑处理类。

状态转移关系类

该类主要用于表示转移函数中的各项,代码如下:

public class Old2NewRelation {

	private String oldState;//当前状态
	private char trans;//读取的字符
	private String newState;//新状态
	private char top;//栈顶字符
	private char putInStack;//要压入栈中的字符
	
	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 Old2NewRelation(String oldState, char trans, String newState, char top, char putInStack) {
		super();
		this.oldState = oldState;
		this.trans = trans;
		this.newState = newState;
		this.top = top;
		this.putInStack = putInStack;
	}
	/* (非 Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "状态转移关系 [当前状态:" + oldState + ", 识别的字符:" + trans + ", 转移到的状态:" + newState + ", 对栈的操作是用:" + putInStack
				+ "替换栈顶元素:" + top + "]";
	}
	public char getPutInStack() {
		return putInStack;
	}
	public void setPutInStack(char putInStack) {
		this.putInStack = putInStack;
	}
	public char getTop() {
		return top;
	}
	public void setTop(char top) {
		this.top = top;
	}	
}

键盘录入及逻辑处理类

  1. 键盘录入
    将输入的字符串特殊处理后存储到字符数组中

    //将字符存入字符数组中
    Scanner sc = new Scanner(System.in);
    String str=sc.next();
    str="#"+str;//特殊处理,用来使栈中刚开始识别输入串时栈中栈顶符号为$
    char cin[] = str.toCharArray();//利用toCharArray方法转换
    sc.close();
    
  2. 初始化状态转移关系集合

    private static ArrayList<Old2NewRelation> initRelation() {
    	//用‘#’表示空字符
    	//分别表示 当前状态-识别字符-转移状态-栈顶元素-替换栈顶的元素
    	ArrayList<Old2NewRelation> list = new ArrayList<Old2NewRelation>();
    	list.add(new Old2NewRelation("q1",'#',"q2",'#','$'));
    	list.add(new Old2NewRelation("q2",'a',"q4",'#','#'));
    	list.add(new Old2NewRelation("q2",'b',"q2",'#','b'));
    	list.add(new Old2NewRelation("q2",'c',"q3",'b','#'));
    	list.add(new Old2NewRelation("q2",'c',"q6",'#','c'));
    	list.add(new Old2NewRelation("q2",'#',"q7",'$','#'));
    	list.add(new Old2NewRelation("q3",'a',"q4",'#','#'));
    	list.add(new Old2NewRelation("q3",'b',"q2",'#','b'));
    	list.add(new Old2NewRelation("q3",'c',"q3",'b','#'));
    	list.add(new Old2NewRelation("q3",'c',"q6",'#','c'));
    	list.add(new Old2NewRelation("q3",'#',"q7",'$','#'));
    	list.add(new Old2NewRelation("q4",'a',"q4",'#','#'));
    	list.add(new Old2NewRelation("q4",'b',"q5",'c','#'));
    	list.add(new Old2NewRelation("q4",'b',"q2",'#','b'));
    	list.add(new Old2NewRelation("q4",'c',"q3",'b','#'));
    	list.add(new Old2NewRelation("q4",'c',"q6",'#','c'));
    	list.add(new Old2NewRelation("q4",'#',"q7",'$','#'));
    	list.add(new Old2NewRelation("q5",'a',"q4",'#','#'));
    	list.add(new Old2NewRelation("q5",'b',"q5",'c','#'));
    	list.add(new Old2NewRelation("q5",'b',"q2",'#','b'));
    	list.add(new Old2NewRelation("q5",'c',"q6",'#','c'));
    	list.add(new Old2NewRelation("q5",'#',"q7",'$','#'));
    	list.add(new Old2NewRelation("q6",'a',"q4",'#','#'));
    	list.add(new Old2NewRelation("q6",'b',"q5",'c','#'));
    	list.add(new Old2NewRelation("q6",'c',"q6",'#','c'));
    	list.add(new Old2NewRelation("q6",'#',"q7",'$','#'));
    	return list;
    }
    
  3. 从前到后依次遍历读取字符
    对于每一个字符都进行下列操作,根据不同的条件,转移到新的状态,并根据实际情况对栈进行出栈或入栈的操作.

    //开始处理逐个识别字符,bc中需要选择一个进行压栈,此处选择对b进行操作
    for (int i = 0; i < cin.length; i++) {
       char cNow = cin[i];
       if (cNow!='#') {
       	System.out.printf("识别第%d个字符%s",i,cNow);
       	System.out.println();
       }else{
       	System.out.println("预制特殊标志$");
       	System.out.println("------------------------------------");
       }			
       isTrave=false;
       for (Old2NewRelation stateTr : list) {
       	if (isTrave) {//状态发生转移之后,就不再遍历,而是对下一个字符重新开始判断
       		break;
       	}
       	if(strState.equals(stateTr.getOldState())&&(cNow==stateTr.getTrans())) {
       		//如果栈空或栈顶字符和当前识别的字符一致(stack.empty()栈的特殊处理也归到这一逻辑中)
       		if (stack.empty()||((char)stack.peek())=='$'||((char)stack.peek())==cNow) {			
       			if (stateTr.getTop()=='#') {
       				strState=stateTr.getNewState();//状态转移到新状态
       				System.out.println(stateTr.toString());
       				isTrave=true;
       				if (stateTr.getPutInStack()!='#') {//不管栈是否为空,只要是非空替换空,就入栈
       					//非空替换空的状态,也就是要压入栈中一个字符								
       					System.out.println("将其压入栈中"+stateTr.getPutInStack());
       					System.out.println("------------------------------------");
       					stack.push(stateTr.getPutInStack());
       				}else {
       					System.out.println("用空替换空,不对栈进行操作");
       					System.out.println("------------------------------------");
       				}
       			}						
       		}else {//栈中有元素,且和输入字符不同
       			strState=stateTr.getNewState();//状态转移到新状态
       			System.out.println(stateTr.toString());
       			isTrave=true;
       			if (stateTr.getTop()!='#') {
       				System.out.println("出栈元素为:"+stack.peek());
       				System.out.println("------------------------------------");
       				stack.pop();
       			}else {
       				System.out.println("栈顶从空到空,不对栈进行操作");
       				System.out.println("------------------------------------");
       			}			
       		}
       	}
       }
    }
    
  4. 全部输入字符串识别完成后判断处理特殊符号$

    isTrave=false;
    if (!stack.empty()&&((char)stack.peek())=='$') {
    	//根据状态转移函数得到最终处理的状态
    	for (Old2NewRelation stateTr : list) {
    		if (isTrave) {
    			break;
    		}
    		if(stateTr.getTop()=='$'&&strState.equals(stateTr.getOldState())&&('#'==stateTr.getTrans())) {
    			isTrave=true;
    			strState=stateTr.getNewState();//状态转移到新状态
    			System.out.println("当前栈顶字符为:"+stack.peek()+"对该标志处理,进行状态转移");
    			System.out.println(stateTr.toString());
    			System.out.println("------------------------------------");
    		}
    	}
    }else {
    	System.out.println("字符串全部识别后,栈顶字符为:"+stack.peek()+"不是预制的栈标志$");
    	System.out.println("------------------------------------");
    }
    if (strState==endState) {
    	System.out.println("字符b和c在字符串中出现的次数相同,能被该PDA正确接受,字符串全部识别完成后在最终状态"+strState);
    } else {
    	System.out.println("字符b和c在字符串中出现的次数不同,被该PDA拒绝,字符串全部识别结束后当前状态为"+strState);
    }
    
  5. 所有处理完成之后,判断当前状态和PDA中的接受状态是否相同,如果相同,表明字符串识别完成后栈中为空并且PDA已经到达了接受状态,这个字符串中b和c出现的次数相同,能够被设计的PDA正确的接受;否则,表明字符串识别完成后栈中非空,并且没有到达设计的PDA的接受状态,这个字符串的b和c出现的次数不同,被设计的PDA拒绝。

测试运行

构造的PDA能够识别的语言L为字符串中b和c出现的次数相同。

输入ccabcbbbca时

当输入字符串为ccabcbbbca时,bc出现的次数相同,符合语言L的定义,此时应该被本次设计的PDA接受。

输入字符串【仅可包含abc】,判断b的数量是否和c的数量相同,相同接受,不同拒绝
ccabcbbbca
PDA的初始状态为:q1结束接收状态为:q7
------------------------------------
预制特殊标志$
------------------------------------
状态转移关系 [当前状态:q1, 识别的字符:#, 转移到的状态:q2, 对栈的操作是用:$替换栈顶元素:#]
将其压入栈中$
------------------------------------
识别第1个字符c
状态转移关系 [当前状态:q2, 识别的字符:c, 转移到的状态:q6, 对栈的操作是用:c替换栈顶元素:#]
将其压入栈中c
------------------------------------
识别第2个字符c
状态转移关系 [当前状态:q6, 识别的字符:c, 转移到的状态:q6, 对栈的操作是用:c替换栈顶元素:#]
将其压入栈中c
------------------------------------
识别第3个字符a
状态转移关系 [当前状态:q6, 识别的字符:a, 转移到的状态:q4, 对栈的操作是用:#替换栈顶元素:#]
栈顶从空到空,不对栈进行操作
------------------------------------
识别第4个字符b
状态转移关系 [当前状态:q4, 识别的字符:b, 转移到的状态:q5, 对栈的操作是用:#替换栈顶元素:c]
出栈元素为:c
------------------------------------
识别第5个字符c
状态转移关系 [当前状态:q5, 识别的字符:c, 转移到的状态:q6, 对栈的操作是用:c替换栈顶元素:#]
将其压入栈中c
------------------------------------
识别第6个字符b
状态转移关系 [当前状态:q6, 识别的字符:b, 转移到的状态:q5, 对栈的操作是用:#替换栈顶元素:c]
出栈元素为:c
------------------------------------
识别第7个字符b
状态转移关系 [当前状态:q5, 识别的字符:b, 转移到的状态:q5, 对栈的操作是用:#替换栈顶元素:c]
出栈元素为:c
------------------------------------
识别第8个字符b
状态转移关系 [当前状态:q5, 识别的字符:b, 转移到的状态:q2, 对栈的操作是用:b替换栈顶元素:#]
将其压入栈中b
------------------------------------
识别第9个字符c
状态转移关系 [当前状态:q2, 识别的字符:c, 转移到的状态:q3, 对栈的操作是用:#替换栈顶元素:b]
出栈元素为:b
------------------------------------
识别第10个字符a
状态转移关系 [当前状态:q3, 识别的字符:a, 转移到的状态:q4, 对栈的操作是用:#替换栈顶元素:#]
用空替换空,不对栈进行操作
------------------------------------
当前栈顶字符为:$对该标志处理,进行状态转移
状态转移关系 [当前状态:q4, 识别的字符:#, 转移到的状态:q7, 对栈的操作是用:#替换栈顶元素:$]
------------------------------------
字符b和c在字符串中出现的次数相同,能被该PDA正确接受,字符串全部识别完成后在最终状态q7

Process finished with exit code 0

输入为cbabcac时

当输入字符串为cbabcac时,bc出现的次数不同,不符合语言L的定义,此时应该被本次设计的PDA拒绝。

输入字符串【仅可包含abc】,判断b的数量是否和c的数量相同,相同接受,不同拒绝
cbabcac
PDA的初始状态为:q1结束接收状态为:q7
------------------------------------
预制特殊标志$
------------------------------------
状态转移关系 [当前状态:q1, 识别的字符:#, 转移到的状态:q2, 对栈的操作是用:$替换栈顶元素:#]
将其压入栈中$
------------------------------------
识别第1个字符c
状态转移关系 [当前状态:q2, 识别的字符:c, 转移到的状态:q6, 对栈的操作是用:c替换栈顶元素:#]
将其压入栈中c
------------------------------------
识别第2个字符b
状态转移关系 [当前状态:q6, 识别的字符:b, 转移到的状态:q5, 对栈的操作是用:#替换栈顶元素:c]
出栈元素为:c
------------------------------------
识别第3个字符a
状态转移关系 [当前状态:q5, 识别的字符:a, 转移到的状态:q4, 对栈的操作是用:#替换栈顶元素:#]
用空替换空,不对栈进行操作
------------------------------------
识别第4个字符b
状态转移关系 [当前状态:q4, 识别的字符:b, 转移到的状态:q2, 对栈的操作是用:b替换栈顶元素:#]
将其压入栈中b
------------------------------------
识别第5个字符c
状态转移关系 [当前状态:q2, 识别的字符:c, 转移到的状态:q3, 对栈的操作是用:#替换栈顶元素:b]
出栈元素为:b
------------------------------------
识别第6个字符a
状态转移关系 [当前状态:q3, 识别的字符:a, 转移到的状态:q4, 对栈的操作是用:#替换栈顶元素:#]
用空替换空,不对栈进行操作
------------------------------------
识别第7个字符c
状态转移关系 [当前状态:q4, 识别的字符:c, 转移到的状态:q6, 对栈的操作是用:c替换栈顶元素:#]
将其压入栈中c
------------------------------------
字符串全部识别后,栈顶字符为:c不是预制的栈标志$
------------------------------------
字符b和c在字符串中出现的次数不同,被该PDA拒绝,字符串全部识别结束后当前状态为q6

Process finished with exit code 0

代码下载及说明

代码下载

该实验的代码已上传到github上,可以 点我下载

说明

该代码针对这一实验完成。遇到其他PDA问题时,需要考虑实际,并进行相应的修改。
PS:代码只是简单实现了逻辑,简单构造解决这一实验描述问题的PDA,只是当时解决问题的一种思路。

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值