对任意给定的NFA M进行确定化操作(附详细注释)

news2024/12/23 17:25:16

对任意给定的NFA M进行确定化操作(附详细注释)

DFA实体类

package Beans;

import java.util.List;

public class DFA {

    private List<Integer> K;    // 状态集

    private char[] letters;     // 字母表

    private String[][] f;       // 转换函数

    private int S;              // 唯一初态

    private List<Integer> Z;    // 终态集

    public List<Integer> getK() {
        return K;
    }

    public void setK(List<Integer> k) {
        K = k;
    }

    public char[] getLetters() {
        return letters;
    }

    public void setLetters(char[] letters) {
        this.letters = letters;
    }

    public String[][] getF() {
        return f;
    }

    public void setF(String[][] f) {
        this.f = f;
    }

    public int getS() {
        return S;
    }

    public void setS(int s) {
        S = s;
    }

    public List<Integer> getZ() {
        return Z;
    }

    public void setZ(List<Integer> z) {
        Z = z;
    }
}

NFA实体类

package Beans;

import java.util.List;

public class NFA {

    private List<Integer> K;    // 状态集

    private char[] letters;     // 字母表

    private String[][] f;       // 转换函数

    private List<Integer> S;    // 初态集

    private List<Integer> Z;    // 终态集

    public List<Integer> getK() {
        return K;
    }

    public void setK(List<Integer> k) {
        K = k;
    }

    public char[] getLetters() {
        return letters;
    }

    public void setLetters(char[] letters) {
        this.letters = letters;
    }

    public String[][] getF() {
        return f;
    }

    public void setF(String[][] f) {
        this.f = f;
    }

    public List<Integer> getS() {
        return S;
    }

    public void setS(List<Integer> s) {
        S = s;
    }

    public List<Integer> getZ() {
        return Z;
    }

    public void setZ(List<Integer> z) {
        Z = z;
    }


}

NFA转换成DFA方法类(NFA2DFA)

package Service;

import Beans.DFA;
import Beans.NFA;

import java.util.*;

public class NFA2DFA {

    /**
     * definite:【子集法】将NFA确定化为DFA
     * @param nfa 输入nfa
     * @return    返回dfa
     *
     * 伪代码逻辑:
     *  DFA状集合C(注:C的每个成员又都是NFA的状态state的集合)
     *  Queue 临时队列queue;
     *  NFA的初态集S(K0)的闭包入队;
     *  while(队不空):
     *      取出当前状态I;
     *      for 每个输入字母 in letters:
     *             nextI = ε-closure(move(I, letter));
     *             if(C 不包含 nextI) :
     *                  则nextI入队
     *
     *  最终的C即为确定化生成的DFA的状态集
     */
    public DFA definite(NFA nfa){
        List<Integer> K0 = nfa.getS();//获取初态集
        char[] letters = nfa.getLetters();//获取字母表
        String[][] f = nfa.getF();//获取转换函数
        List<Integer> Z = nfa.getZ();//获取终态集

        DFA dfa = new DFA(); //new一个DFA对象
        List<Integer> K = new ArrayList<>();             // DFA状态集合(已重命名)
        int k = 0;                                       // 用于DFA状态集的重命名
        List<String[]> listF = new ArrayList<>();        // 用于暂存转换函数f
        List<Integer> listZ = new ArrayList<>();         // 用于暂存终态集Z
        StringBuilder sb = new StringBuilder();          // 用于输出整个子集法的过程

        Set<List<Integer>> set = new HashSet<>();               // 状态集临时集合
        Queue<List<Integer>> queue = new LinkedList<>();        // 状态集临时队列
        Map<List<Integer>, Integer> map = new HashMap<>();      //「状态集」向「命名」的映射

        List<Integer> closure_K = closure(K0, f); //初始状态自身以及自身能通过ε'到达的状态'的集合。
        K.add(k);//将0作为一个状态
        map.put(closure_K, k++);//将能到达的状态放入映射接口
        queue.add(closure_K);//获得的能到达的状态集放入队列
        while(!queue.isEmpty()){
            List<Integer> I = queue.poll();//poll()返回队首元素的同时删除队首元素,队列为空时返回NULL,I为能到达的状态集
            for(char letter : letters){//遍历字母表
                List<Integer> nextI = closure(move(I, letter, f), f);//当前状态集通过某个字母可以转化到的下一状态集(一步转换,没有进行ε-闭包运算)
                //再进行闭包运算
                if(nextI.isEmpty()) continue;//跳出此次循环,执行下一次循环
                if(!containsI(set, nextI)){//如果不包含,则执行
                    nextI.sort(Comparator.comparing(Integer::intValue));//正序排序
                    map.put(nextI, k);
                    K.add(k);
                    if(!Collections.disjoint(nextI, Z)){//检查两个集合是否相交
                        listZ.add(k);//相交的话将k放入暂存的终态集中
                    }
                    set.add(nextI);//放入set中
                    queue.add(nextI);
                    k++;
                }
                System.out.print(I);
                System.out.println(nextI);
                listF.add(new String[]{String.valueOf(map.get(I)), String.valueOf(letter), String.valueOf(map.get(nextI))});
                sb.append(map.get(I)).append(letter).append(map.get(nextI)).append('\n');//保存整个子集法的过程
            }
        }
        System.out.println("重命名后:");
        System.out.println(sb.toString());//输出整个子集法的过程

        // 下面是为了构造出DFA对象
        int len = K.size();
        String[][] f2 = new String[len][len];
        for(String[] tmp : f2){
            Arrays.fill(tmp, "");
        }
        for(String[] arr : listF){
            f2[Integer.parseInt(arr[0])][Integer.parseInt(arr[2])] += arr[1];
        }
        dfa.setK(K);// 状态集
        dfa.setLetters(letters);//字母表
        dfa.setF(f2);//转换函数
        dfa.setS(0);//初态集
        dfa.setZ(listZ);//终态集
        return dfa;
    }

    /**
     * ε-closure闭包运算:某个状态集经过任意多个ε,得到当前的真正状态集
     * @param I 当前状态集
     * @return 当前真正的状态集(closureI)
     */
    private List<Integer> closure(List<Integer> I, String[][] f){
        // 经过任意多个ε,因此BFS
        List<Integer> closureI  = new ArrayList<>();
        Queue<Integer> queue = new LinkedList<>();
        for(int i : I){//遍历当前状态集
            queue.add(i);//进入队列
            while (!queue.isEmpty()){//队列不为空则执行循坏
                int n = queue.poll();//poll()返回队首元素的同时删除队首元素,队列为空时返回NULL
                for(int iNext = 0; iNext < f.length; iNext++){
                    for(char c : f[n][iNext].toCharArray()){
                        if(c == 'ε' && !closureI.contains(iNext)){//一个个字符对比,比如‘εb’则需要比较两次
                            closureI.add(iNext);//经过对角元素ε,得到真正的状态集
                            if(n != iNext){
                                queue.add(iNext);
                            }
                        }
                    }
                }
            }
        }
        return closureI;
    }

    /**
     * move方法:当前状态集通过某个字母可以转化到的下一状态集(一步转换,没有进行ε-闭包运算)
     * @param I 当前的状态集,letter 字母表,f 转换函数
     * @return 返回下一状态集
     */
    private List<Integer> move(List<Integer> I, char letter, String[][] f){
        List<Integer> nextI = new ArrayList<>();
        for(int i : I){
            for(int iNext = 0; iNext < f.length; iNext++){//遍历转换函数
                for(char c : f[i][iNext].toCharArray()){//读取对应于可到达状态的转换函数
                    if(c == letter && !nextI.contains(iNext)) {//一一对比
                        nextI.add(iNext);//放入下一状态集的集合中
                    }
                }
            }
        }
        return nextI;
    }


    /**
     * 判断一个Set<List>是否包含某个List
     * @param set
     * @param list
     */
    private boolean containsI(Set<List<Integer>> set, List<Integer> list){
        for(List<Integer> l : set){
            if(listEquals(l, list)){
                return true;
            }
        }
        return false;
    }

    /**
     * 判断两个List集合是否相等(不考虑顺序)
     * @param list1
     * @param list2
     */
    private boolean listEquals(List<Integer> list1, List<Integer> list2){
        list1.sort(Comparator.comparing(Integer::intValue));
        list2.sort(Comparator.comparing(Integer::intValue));
        return list1.toString().equals(list2.toString());
    }

}

输入函数类

package Utils;

import Beans.DFA;
import Beans.NFA;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

/**
 * EnterUtils工具类————用于从外部获取输入,然后set给传入的nfa对象
 */
public class EnterUtils {

    /**
     * 状态集的个数(转换函数f也要用到)
     * 这个n被所有nfa类共享————为了防止出错,enterF一定要写在enterK的后面,才能保证初始化f时使用正确的n
     */
    private static int n;

    // 输入状态集
    public static void enterK(NFA nfa) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));//从缓冲区中读取内容
        List<Integer> list = new ArrayList<>();//创建一个动态数组
        String[] strings = br.readLine().split("");// 一行行读取,一行放入一个字符串数组中
        for(String s : strings){
            list.add(Integer.parseInt(s));
        }//将状态集一一放入列表中
        n = list.size();//获取列表长度
        nfa.setK(list);//放入NFA状态集对应列表
    }

    // 输入字母表
    public static void enterLetters(NFA nfa) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));//从缓冲区中读取内容
        char[] letters = br.readLine().toCharArray();//将字符串转换为字符数组
        nfa.setLetters(letters);//放入NFA字母表对应数组
    }

    // 输入转换函数
    public  static void enterF(NFA nfa) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));//从缓冲区中读取内容
        String[][] f = new String[n][n];//n状态集长度,二维字符串数组
        for(String[] arr : f){
            Arrays.fill(arr, "");
        }//arr字符串数组初始化为空
        for(int i = 0; i < f.length; i++){
            f[i][i] = "ε";
        }//将二维字符串数组的对角元素赋值为空字符ε
        String line = "";
        while(!(line = br.readLine().trim()).equals("end")){//读取一行,直到输入"end"结束
            String[] arr = line.split("");
            f[Integer.parseInt(arr[0])][Integer.parseInt(arr[2])] += arr[1];//赋值
        }
        nfa.setF(f);//放入NFA转换函数对应二维字符串数组
    }

    // 输入初态集
    public static void enterS(NFA nfa) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));//从缓冲区中读取内容
        List<Integer> list = new ArrayList<>();//创建一个动态数组
        String[] strings = br.readLine().split("");// 一行行读取,一行放入一个字符串数组中
        for(String s : strings){//遍历字符串数组
            list.add(Integer.parseInt(s));//将字符强行转换成整型后放入列表中
        }
        nfa.setS(list);//放入NFA初态集对应的整型列表中
    }

    // 输入终态集
    public static void enterZ(NFA nfa) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));//从缓冲区中读取内容
        List<Integer> list = new ArrayList<>();//创建一个动态数组
        String[] strings = br.readLine().split("");// 一行行读取,一行放入一个字符串数组中
        for (String s : strings){
            list.add(Integer.parseInt(s));//将字符强行转换成整型后放入列表中
        }
        nfa.setZ(list);//放入NFA终态集对应的整型列表中
    }

}

输出函数类

package Utils;

import Beans.DFA;
import Beans.NFA;

import java.util.Arrays;

/**
 * EnterUtils工具类————用于打印nfa对象
 */
public class DisplayUtils {

    public static void displayK(NFA nfa){
        System.out.println(nfa.getK());
    }

    public static void displayLetters(NFA nfa){
        System.out.println(Arrays.toString(nfa.getLetters()));
    }

    public static void displayF(NFA nfa){
        String[][] f = nfa.getF();
        for(int i = 0; i < f.length; i++){
            for(int j = 0; j < f.length; j++){
                if(f[i][j] == "")  continue;
                for(char c : f[i][j].toCharArray()){
                    if(i == j && c == 'ε')  continue;
                    StringBuilder sb = new StringBuilder().append(i).append(c).append(j).append("  ");
                    System.out.print(sb.toString());
                }
            }
        }
        System.out.println();
    }

    public static void displayS(NFA nfa){
        System.out.println(nfa.getS());
    }

    public static void displayZ(NFA nfa){
        System.out.println(nfa.getZ());
    }


    public static void displayK(DFA dfa){
        System.out.println(dfa.getK());
    }

    public static void displayLetters(DFA dfa){
        System.out.println(Arrays.toString(dfa.getLetters()));
    }

    public static void displayF(DFA dfa){
        String[][] f = dfa.getF();
        for(int i = 0; i < f.length; i++){
            for(int j = 0; j < f.length; j++){
                if(f[i][j] == "") continue;
                for(char c : f[i][j].toCharArray()){
                    StringBuilder sb = new StringBuilder().append(i).append(c).append(j).append("  ");
                    System.out.print(sb.toString());
                }
            }
        }
        System.out.println();
    }

    public static void displayS(DFA dfa){
        System.out.println(dfa.getS());
    }

    public static void displayZ(DFA dfa){
        System.out.println(dfa.getZ());
    }

    // 一个小小的细节:
    // 打印NFA的状态转移函数时,需要将自身通过ε向自身的转化删掉
    // 而DFA压根没有ε
    //
    // 这个细节也使得,DFA是不需要ε-closure闭包运算的————move就可以唯一确定下一状态

}

测试类

package Test;

import Beans.DFA;
import Beans.NFA;
import Service.NFA2DFA;
import Utils.DisplayUtils;
import Utils.EnterUtils;

import java.io.IOException;

public class Test_NFA2DFA {
    public static void main(String[] args) throws IOException {

        NFA nfa = new NFA();

        System.out.print("请输入NFA状态集:");          EnterUtils.enterK(nfa);
        System.out.print("请输入NFA字母表:");          EnterUtils.enterLetters(nfa);
        System.out.print("请输入NFA状态转换函数:");     EnterUtils.enterF(nfa);
        System.out.print("请输入NFA初态集:");          EnterUtils.enterS(nfa);
        System.out.print("请输入NFA终态集:");          EnterUtils.enterZ(nfa);

        DFA dfa = new NFA2DFA().definite(nfa);

        System.out.print("DFA状态集:");            DisplayUtils.displayK(dfa);
        System.out.print("DFA字母表:");            DisplayUtils.displayLetters(dfa);
        System.out.print("DFA状态转换函数:");       DisplayUtils.displayF(dfa);
        System.out.print("DFA唯一初态:");          DisplayUtils.displayS(dfa);
        System.out.print("DFA终态集:");            DisplayUtils.displayZ(dfa);

    }

}

运行结果图片

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/643184.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

每天五分钟机器学习:梯度下降的学习率太大或太小会有什么问题?

本文重点 我们前面学习了梯度下降算法,其中有一个重要的参数就是学习率。在使用梯度下降算法时,学习率是一个非常重要的参数。学习率的大小会直接影响梯度下降算法的收敛速度和精度。如果学习率太大或太小,都会对梯度下降算法的表现产生负面影响。 学习率太大的影响 学习率…

Debezium系列之:记录一次生产环境SQLServer数据库删除日志文件造成debezium connector数据不采集的解决方法

Debezium系列之:记录一次生产环境SQLServer数据库删除日志文件造成debezium connector数据不采集的解决方法 一、背景二、快速定位问题三、详细的解决步骤四、确认debezium connector恢复对数据库的数据采集五、经验总结一、背景 SQLServer数据库的日志把磁盘打满了,需要删除…

空间计算时代下,中国能否诞生下一个“苹果”?

“one more thing&#xff01;” 6月6日的WWDC大会上&#xff0c;苹果CEO库克激动地喊出这句乔布斯的口头禅。随后&#xff0c;苹果的Vision Pro头显产品正式亮相&#xff0c;库克形容它是“革命性产品”“开启空间计算时代”。 当一个类似滑雪镜的头显设备出现在屏幕&#x…

Spark大数据处理学习笔记1.5 掌握Scala内建控制结构

文章目录 一、学习目标二、条件表达式&#xff08;一&#xff09;语法格式&#xff08;二&#xff09;执行情况&#xff08;三&#xff09;案例演示任务1、根据输入值的不同进行判断任务2、编写Scala程序&#xff0c;判断奇偶性 三、块表达式&#xff08;一&#xff09;语法格式…

[C国演义] 第二章

第二章 目标和题意分析步骤dp的含义递推公式dp数组初始化遍历顺序 代码 目标和 力扣链接 给你一个整数数组 nums 和一个整数 target 。 向数组中的每个整数前添加 ‘’ 或 ‘-’ &#xff0c;然后串联起所有整数&#xff0c;可以构造一个 表达式 &#xff1a; 例如&#xff0…

充电桩测试系统厂家TK4860B非车载充电机检定装置

温湿度测量&#xff08;选配件&#xff09;&#xff1a;可选配蓝牙温湿度计&#xff0c;通过蓝牙连接平板电脑&#xff0c;完成现场温湿度测量&#xff0c;并自动记录数据。 无线平板操作&#xff0c;可通过拍照保存现场信息&#xff0c;完成自动测试。 一体式结构&#xff0…

qss中样式表不生效

qt问题记录&#xff1a; 1.widget是被提升的窗口&#xff08;被提升为Form&#xff09;&#xff0c;我设置了样式表后它的效果如下图&#xff0c;但是实际跑的时候它的样式表没有显示。 当我取消提升后&#xff08;让它变成普通的QWidget后)&#xff0c;它就正常显现了。 我突…

buuctf re

基本汇编指令mov A B **将B的值复制到A里面去**push A **将A压栈**pop A **将A从栈中弹出来**call Funtion **跳转到某函数**ret --> 相当于 pop ip **从栈中pop出一个值放到EIP里面**je jz **如果ZF&#xff08;0标志位&#xff09;1&#xf…

AI实战营第二期 第七节 《语义分割与MMSegmentation》——笔记8

文章目录 摘要主要特性 案例什么是语义分割应用&#xff1a;无人驾驶汽车应用&#xff1a;人像分割应用&#xff1a;智能遥感应用 : 医疗影像分析 三种分割的区别语义分割的基本思路按颜色分割逐像素份分类全卷积网络 Fully Convolutional Network 2015存在问题 基于多层级特征…

基于Java医院门诊挂号系统设计与实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a; ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精…

02-Vue中的v-show和v-if

前言 一、v-show与v-if的共同点 我们都知道在 vue 中 v-show 与 v-if 的作用效果是相同的(不含v-else)&#xff0c;都能控制元素在页面是否显示 在用法上也是相同的 <Model v-show "isShow"/> <Model v-if"isShow"/>当表达式为true的时候&…

SpringSecurity过滤指定url【.antMatchers(***).permitAll()】失效问题

SpringSecurity过滤指定url【.antMatchers(***).permitAll()】失效问题 问题描述 在使用SpringSecurity作为后端验证框架时&#xff0c;遇到配置一些接口不需要token验证&#xff0c;直接放行&#xff0c;但是配置之后没有生效&#xff0c;一直究其原因。 项目配置 因为要进…

轻松掌握mybatis扩展点

MyBatis提供4个扩展点&#xff1a; 1、Executor执行器接口扩展点 用于执行一系列的SQL操作&#xff0c;底层是使用StatementHandler接口进行原子的SQL操作&#xff0c;Executor在StatementHandler之上进行了一层包装&#xff0c;比如ReuseExecutor执行器&#xff0c;在上层包…

大数据离线阶段

目录 数分分布式Apache ZooKeeperApache HadoopShell 命令选项数据仓库Hive 数分 数据分析的目的是把隐藏在数据背后的信息集中和提炼出来&#xff0c;总结出所研究 对象的内在规律&#xff0c;帮助管理者进行有效的判断和决策。 目的&#xff1a;提炼信息&#xff0c;找出规律…

清华大学出版社618大促《IT系列丛书》

&#x1f482; 个人网站:【海拥】【摸鱼游戏】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 1.《网络安全应急管理与技术…

win11 x86 系统部署arm架构的虚拟机(银河麒麟为例)

文章目录 一、使用 win11 x86 系统部署arm架构的虚拟机&#xff08;银河麒麟为例&#xff09;1.1.1、前言1.1.2、准备环境1.1.1.1、首先&#xff0c;需要一个银河麒麟的arm镜像1.1.1.2、QEMU 软件的下载地址1.1.1.3、UEFI&#xff08;BIOS的替代方案&#xff09;的下载地址1.1.…

Windows编程课设(C#)——基于WPF和.net的即时通讯系统(仿微信)

一款参考QQ、微信的即时通讯软件。采用CS结构&#xff0c;客户端基于.Net与WPF开发&#xff0c;服务端使用Java开发。 提供的服务 注册功能&#xff1a;用户通过手机号码进行账号的注册&#xff0c;每个手机号唯一对应一个用户&#xff0c;需要接收验证码进行手机的绑定。此外…

强化学习笔记-0910 On-policy Method with Approximation

前几章我们所讨论的强化学习方法都是将价值函数建模为一个table形式&#xff0c;通过状态来查询具体状态的价值。但是当状态-动作空间极大&#xff0c;且多数状态-动作并没有太大意义时&#xff0c;这种table查询效率是极低的。 因此本节是将价值函数建模为一个参数模型&#…

修改文件权限chown/chgrp/chmod【Linux】

文章目录 Linux当中的权限问题Linux权限的概念切换用户账号 Linux权限管理文件类型和访问权限&#xff08;事物属性&#xff09; Linux文件名后缀文件权限值的表示方法 文件访问权限的相关设置方法如何改变文件的访问权限如何改变文件的拥有者如何改变文件的所属组如何修改文件…

leetcode 152.乘积最大子数组

题目描述 给你一个整数数组 nums &#xff0c;请你找出数组中乘积最大的非空连续子数组&#xff08;该子数组中至少包含一个数字&#xff09;&#xff0c;并返回该子数组所对应的乘积。 测试用例的答案是一个 32-位 整数。 子数组 是数组的连续子序列。 来源&#xff1a;力扣&a…