📜  自动机理论 | 7套(1)

📅  最后修改于: 2023-12-03 15:41:23.988000             🧑  作者: Mango

自动机理论 | 7套

自动机理论是计算机科学中的一个重要分支,它研究的是在一些规则下,某个输入是否能被接受。自动机理论在编译器、代码优化和计算机网络等领域有着广泛的应用。本文将介绍7套自动机理论相关的内容,涵盖了基础的自动机概念、有限状态机、正则表达式、DFA与NFA转换、最小化DFA、语法分析以及自动机学习等方面的知识。

1. 基础概念

自动机理论起源于20世纪初的逻辑学与数学研究,是一种用于描述符号串处理机制的形式化工具。自动机就是计算机科学中,对识别一些符号串所使用的一种抽象数学模型。

自动机理论的基础概念包括有限自动机、非确定有限自动机、正则表达式、上下文无关文法等。有限自动机(Finite Automata)是自动机理论中最简单,也是最基础的模型之一,它由五元组(Q, Σ, δ, q0, F)组成,其中Q是状态集合,Σ是输入符号集合,δ是状态转移函数,q0 是初始状态,F 是接收状态集合。

class FA:
    def __init__(self, states, sigma, delta, start_state, final_states):
        self.states = states
        self.sigma = sigma
        self.delta = delta
        self.start_state = start_state
        self.final_states = final_states
2. 有限状态机

有限状态机(Finite State Machine, FSM)是一种非常重要的自动机, 它根据一些输入符号序列,对其进行状态转移,并根据最终状态是否在接收状态集合来决定是否接受这些符号序列。有限状态机一般由状态集合、输入符号集合、状态转移函数以及初始状态和接收状态集合组成。有限状态机又被分为确定有限状态机(Deterministic Finite Automata, DFA)和非确定有限状态机(Non-deterministic Finite Automata, NFA)两种。

class FSM:
    def __init__(self):
        self.states = set() # 状态集合
        self.alphabet = set() # 输入符号集合
        self.transitions = dict() # 状态转移函数
        self.start_state = None # 初始状态
        self.final_states = set() # 接收状态集合
3. 正则表达式

正则表达式(Regular Expression, RE)是一种使用单个字符串来描述、匹配一系列符合某个句法规则的字符串的算法。通过对正则表达式的分析可以得到一个能够匹配这个正则表达式的自动机,也就是正则自动机。正则表达式的基本操作包括字符匹配、或操作、连续操作以及重复操作等。

class RE:
    def __init__(self):
        self.start = None
        self.end = None

class Char(RE):
    def __init__(self, c):
        super().__init__()
        self.c = c

class Or(RE):
    def __init__(self, left, right):
        super().__init__()
        self.left = left
        self.right = right

class Concat(RE):
    def __init__(self, left, right):
        super().__init__()
        self.left = left
        self.right = right

class Repeat(RE):
    def __init__(self, child):
        super().__init__()
        self.child = child
4. DFA与NFA转换

有限状态机中NFA与DFA转换是非常重要的一环。在实际应用中,对于某些字符串,如果我们可以直接构造出 DFA 来执行匹配,那么绝对不能浪费时间用 NFA 来匹配。不过,有的正则表达式,只能用 NFA 进行直接匹配,所以学会将 NFA 转 DFA 也非常重要。

def nfa2dfa(nfa):
    start_state = nfa.ep_closure(nfa.start_state)
    states = {start_state}
    new_states = {start_state}
    sigma = nfa.sigma
    delta = dict()
    final_states = set()
    
    while new_states:
        state = new_states.pop()
        
        for a in sigma:
            next_state = nfa.move(state, a)
            next_state = nfa.ep_closure(next_state)
                    
            if not next_state:
                continue
            
            delta[state, a] = next_state
            
            if next_state not in states:
                states.add(next_state)
                new_states.add(next_state)
        
        if nfa.final_state in state:
            final_states.add(state)
                
    start_state = tuple(start_state)
    dfa = DFA(states, sigma, delta, start_state, final_states)
    return dfa
5. 最小化DFA

对于一个 DFA 状态集 S, 可以令 S 分成两个子集 P 和 Q,将集合划分的依据是两个集合中的状态对于同一输入符号时遇到的下一个状态是否处于对方的集合中。如果 P 和 Q 不一致,那么就把 S 继续分为三个子集,如果全部满足了(也就是子集不再变化),这个过程就停止了。由于可能存在多种划分方式,要找到最小 DFA,就需要比较它们。

def minimize_dfa(dfa):
    states = dfa.states
    sigma = dfa.sigma
    delta = dfa.delta
    start_state = dfa.start_state
    final_states = set(dfa.final_states)
    
    partition = [final_states, states-final_states]
    
    while True:
        new_partition = []
        for p in partition:
            for a in sigma:
                p1 = set()
                for state in p:
                    next_state = delta.get((state, a), None)
                    if next_state is not None:
                        p1.add(next_state)
                        
                for q in partition:
                    if len(p1&q) > 0 and len(p-p1&q) > 0:
                        new_partition.append(p1)
                        new_partition.append(p-p1)
                        break
                        
                else:
                    continue
                break
            else:
                new_partition.append(p)
                    
        if new_partition == partition:
            break
            
        partition = new_partition
        
    new_states = []
    new_start = None
    new_finals = set()
    new_delta = dict()
    
    for part in partition:
        if start_state in part:
            new_start = part
            
        for state in part:
            if state in final_states:
                new_finals.add(part)
                
            new_delta[part] = dict()
            for a in sigma:
                next_state = delta.get((state, a), None)
                if next_state is not None:
                    for part1 in partition:
                        if next_state in part1:
                            new_delta[part][a] = part1
                            break
    
    queue = [new_start]
    visited = {new_start}
    
    while queue:
        state = queue.pop(0)
        new_states.append(state)
        
        for a in sigma:
            next_state = new_delta[state].get(a, None)
            
            if next_state is not None and next_state not in visited:
                visited.add(next_state)
                queue.append(next_state)
        
    return DFA(new_states, sigma, new_delta, new_start, new_finals)
6. 语法分析

语法分析是编译器的重要部分,它将输入的字符串分析成一个 parse 树或者抽象语法树(AST)。语法分析常常使用自动机,它会根据词语的序列和一个文法来构建一个自动机,将输入串输入该自动机得到一个转移序列,依据此序列来给输入串归纳一颗解析树。

class ParseTree:
    def __init__(self, value):
        self.value = value
        self.children = []
        
    def add_child(self, child):
        self.children.append(child)
        
class SyntaxAnalyzer:
    def __init__(self, grammar):
        self.grammar = grammar
        self.first = self.get_first()
        self.follow = self.get_follow()
        
    def get_first(self): 
        first = dict()
        for rule in self.grammar:
            first[rule.left] = set()
            
        for symbol in self.grammar.terminals:
            first[symbol].add(symbol)
            
        for symbol in self.grammar.nonterminals:
            if not first.get(symbol):
                first[symbol] = set()
                
        prev = dict()
        
        while prev != first:
            prev = first.copy()
            for rule in self.grammar:
                nonterminal = rule.left
                for production in rule.productions:
                    for symbol in production:
                        first[nonterminal].update(first.get(symbol, set()))
                        if EPS not in first.get(symbol, set()):
                            break
                    else:
                        first[nonterminal].add(EPS)
                        
        return first
        
    def get_follow(self):
        follow = dict()
        for symbol in self.grammar.nonterminals:
            follow[symbol] = set()
       
        follow[self.grammar.start].add(DOLLAR)

        while True:
            prev = follow.copy()
            for rule in self.grammar:
                left = rule.left
                for i, symbol in enumerate(rule.productions):
                    if symbol in self.grammar.nonterminals:
                        follow[symbol].update(prev.get(left, set()))
                        if EPS in self.first.get(rule.productions[i+1], {EPS}):
                            follow[symbol].update(prev.get(rule.left, set()))
                
            if prev == follow:
                break
                
        return follow
        
    def parse(self, tokens):
        stack = [ParseTree(DOLLAR), ParseTree(self.grammar.start)]
        index = 0
        
        while stack:
            node = stack.pop()
            
            if node.value in self.grammar.terminals:
                token = tokens[index]
                index += 1
                while token == EPS:
                    token = tokens[index]
                    index += 1
                if node.value != token:
                    return None
                
            else: 
                rule = self.grammar.rules.get(node.value).get(tokens[index], None)
                
                if rule is None:
                    return None
                
                node.value = rule.left
                for symbol in reversed(rule.productions):
                    child = ParseTree(symbol)
                    node.add_child(child)
                    stack.append(child)
        
        return node
7. 自动机学习

自动机学习(Automata Learning)是指通过询问和观察样本来学习自动机的过程。它一般包括基于状态与条件的假设、反例的构造等具体步骤。自动机学习在自动化测试、串匹配、自然语言分析等领域有着广泛的应用。

class Teacher:
    def __init__(self, machine):
        self.machine = machine
        
    def equivalence_query(self, hypothesis):
        counter_example = None
        inputs = ''.join(self.machine.sigma)
        while counter_example is None:
            result = self.machine.test_equivalence(hypothesis)
            if result is None:
                return None
            
            query, expected = result
            output = hypothesis.process_input(query)
            
            if output != expected:
                counter_example = (query, output)
                
        return counter_example
    
    def membership_query(self, query):
        output = self.machine.process_input(query)
        return output
总结

自动机理论是计算机科学中非常重要的一个分支,本文为大家介绍了7套自动机理论相关的内容,包括了基础的自动机概念、有限状态机、正则表达式、DFA与NFA转换、最小化DFA、语法分析以及自动机学习等方面的知识。希望本文能够为读者对自动机理论有更全面的认识,也希望对读者在实际应用中有所帮助。