有限自动机是如何在代码中实现的?

Posted

技术标签:

【中文标题】有限自动机是如何在代码中实现的?【英文标题】:How are finite automata implemented in code? 【发布时间】:2016-05-18 06:57:07 【问题描述】:

如何在 Python 代码中实现 DFANFA

在 Python 中有哪些好的方法可以做到这一点?它们是否曾在现实世界的项目中使用过?

【问题讨论】:

这个问题非常广泛。除非您可以将其缩小到特定的客观可回答的问题,否则它可能会被关闭。无论如何......是的,它们用于现实世界的项目。 “状态机”是一种常见的实现技术。这是python中一种可能的方法:python-3-patterns-idioms-test.readthedocs.org/en/latest/… 真正的正则表达式可以被DFA匹配;事实上,任何正则语言都可以表示为正则表达式或 DFA。 【参考方案1】:

表示 DFA 的一种直接方式是使用字典字典。对于每个状态,创建一个由字母表中的字母作为键的字典,然后是一个由状态键的全局字典。例如,Wikipedia article on DFAs 中的以下 DFA

可以用这样的字典来表示:

dfa = 0:'0':0, '1':1,
       1:'0':2, '1':0,
       2:'0':1, '1':2

针对从相关字母表中提取的输入字符串“运行”一个 dfa(在指定初始状态和接受值集之后)很简单:

def accepts(transitions,initial,accepting,s):
    state = initial
    for c in s:
        state = transitions[state][c]
    return state in accepting

您从初始状态开始,逐个字符地遍历字符串,并在每一步中简单地查找下一个状态。当您完成对字符串的单步执行后,您只需检查最终状态是否处于接受状态集。

例如

>>> accepts(dfa,0,0,'1011101')
True
>>> accepts(dfa,0,0,'10111011')
False

对于 NFA,您可以在转换字典中存储可能状态集而不是单个状态,并使用 random 模块从可能状态集中选择下一个状态。

【讨论】:

【参考方案2】:

在这里,我提出了 NFA 的递归解决方案。考虑以下 nfa:

可以使用列表列表表示转换,如下所示:

transition = [[[0,1],[0]], [[4],[2]], [[4],[3]], [[4],[4]],[[4],[4]]]

注意:状态 4 是一种假设状态。一旦你进入那个状态,你就不能再进一步了。当您无法从当前状态读取输入时,它会很有帮助。您直接进入状态 4 并说当前进度不接受输入(返回检查其他可能性)。例如,如果您在q1,并且当前输入符号是'a',则进入状态4并停止进一步计算。

这是 Python 代码:

#nfa simulation for (a|b)*abb
#state 4 is a trap state


import sys

def main():
    transition = [[[0,1],[0]], [[4],[2]], [[4],[3]], [[4],[4]]]
    input = raw_input("enter the string: ")
    input = list(input) #copy the input in list because python strings are immutable and thus can't be changed directly
    for index in range(len(input)): #parse the string of a,b in 0,1 for simplicity
        if input[index]=='a':
            input[index]='0' 
        else:
            input[index]='1'

    final = "3" #set of final states = 3
    start = 0
    i=0  #counter to remember the number of symbols read

    trans(transition, input, final, start, i)
    print "rejected"



def trans(transition, input, final, state, i):
    for j in range (len(input)):
        for each in transition[state][int(input[j])]: #check for each possibility
            if each < 4:                              #move further only if you are at non-hypothetical state
                state = each
                if j == len(input)-1 and (str(state) in final): #last symbol is read and current state lies in the set of final states
                    print "accepted"
                    sys.exit()
                trans(transition, input[i+1:], final, state, i) #input string for next transition is input[i+1:]
        i = i+1 #increment the counter


main()

示例运行(接受以 abb 结尾的字符串):

enter the string: abb
accepted

enter the string: aaaabbbb
rejected

【讨论】:

【参考方案3】:

如果您使用递归,则不需要 for 循环覆盖 range(len(input))。您使代码过于复杂。这是一个简化版

import sys

def main():
    transition = [[[0,1],[0]], [[4],[2]], [[4],[3]], [[4],[4]]]
    input = raw_input("enter the string: ")
    input = list(input) #copy the input in list because python strings are immutable and thus can't be changed directly
    for index in range(len(input)): #parse the string of a,b in 0,1 for simplicity
        if input[index]=='a':
            input[index]='0' 
        else:
            input[index]='1'

    final = "3" #set of final states = 3
    start = 0

    trans(transition, input, final, start)
    print "rejected"


def trans(transition, input, final, state):
    for each in transition[state][int(input[0])]: #check for each possibility       
        if each < 4:                              #move further only if you are at non-hypothetical state
            state = each
            if len(input)==1:
                if (str(state) in final): #last symbol is read and current state lies in the set of final states
                    print "accepted"
                    sys.exit()
                else:
                    continue
            trans(transition, input[1:], final, state) #input string for next transition is input[i+1:]

main()

【讨论】:

【参考方案4】:

如果您正在寻找更面向对象的实现,这是我的 dfa 实现版本。然而,约翰·科尔曼的回答让我受到了如此轻微的启发。

class Node:
    def __init__(self, val):
        self.val = val
        self.links = []
    def add_link(self, link):
        self.links.append(link)
    def __str__(self):
        node = "(%s):\n" % self.val
        for link in self.links:
            node += "\t" + link + "\n"
        return node
    def __add__(self, other):
        return str(self) + other
    def __radd__(self, other):
        return other + str(self)
    def equals(self, node):
        ok = (self.val == node.val)
        if len(self.links) == len(node.links):
            for i in range(len(self.links)):
                ok = ok and (self.links[i] == node.links[i])
            return ok
        else:
            return False

class Link:
    def __init__(self, from_node, etiquette, to_node):
        self.from_node = from_node
        self.etiquette = etiquette
        self.to_node = to_node
    def __str__(self):
        return "(%s --%s--> %s)" % (self.from_node.val, self.etiquette, self.to_node.val)
    def __add__(self, other):
        return str(self) + other
    def __radd__(self, other):
        return other + str(self)
    def equals(self, link):
        return (self.from_node == link.from_node) and (self.etiquette == link.etiquette) and (self.to_node == link.to_node)

class Automata:
    def __init__(self, initial_node, nodes, terminal_node):
        self.initial_node = initial_node
        self.nodes = nodes
        self.terminal_node = terminal_node
    def get_next_node(self, current_node, etiquette):
        for link in current_node.links:
            if link.etiquette == etiquette:
                return link.to_node
        return None
    def accepts(self, string):
        node = self.initial_node
        for character in string:
            node = self.get_next_node(node, character)
        return self.terminal_node.equals(node)
    def __str__(self):
        automata = "Initial node: %s\nTerminal node: %s\n" % (self.initial_node.val, self.terminal_node.val)
        for node in self.nodes:
            automata += node
        return automata
    def __add__(self, other):
        return str(self) + other
    def __radd__(self, other):
        return other + str(self)




if __name__ == '__main__':
    pass

    s0 = Node("s0")
    s1 = Node("s1")
    s2 = Node("s2")

    s0_0_s0 = Link(s0, '0', s0)
    s0_1_s1 = Link(s0, '1', s1)
    s1_0_s2 = Link(s1, '0', s2)
    s1_1_s0 = Link(s1, '1', s0)
    s2_0_s1 = Link(s2, '0', s1)
    s2_1_s2 = Link(s2, '1', s2)

    s0.add_link(s0_0_s0)
    s0.add_link(s0_1_s1)
    s1.add_link(s1_0_s2)
    s1.add_link(s1_1_s0)
    s2.add_link(s2_0_s1)
    s2.add_link(s2_1_s2)

    a = Automata(s0, [s0, s1, s2], s0)

    print(a)
    print(a.accepts('1011101')) #True
    print(a.accepts('10111011')) #False

【讨论】:

【参考方案5】:

我已经实现了适用于任何 dfa 的 dfa。但这不是面向对象的模式。

states=list(map(int,input("Enter States : ").split(" ")))
symbols=list(input("Enter Symbols : ").split(" "))
initial_state=int(input("Enter initial state : "))
final_states=list(map(int,input("Enter final states : ").split(" ")))

transitions=[]
inlists=[]

for i in range(len(symbols)):
    print("Enter transitions for symbol "+symbols[i]+" from all states :")
    for j in range(len(states)):
        inlists.append(int(input()))
    transitions.append(inlists)
    inlists=[]
cur_state=initial_state

while(1):
    cur_state=initial_state
    string=input("Enter string : ")

    if string=='#':
        break

    for symbol in string:
        print("["+str(cur_state)+"]"+"-"+symbol+"->",end="")
        cur_state=transitions[symbols.index(symbol)][cur_state]

    if cur_state in final_states:
        print("["+str(cur_state)+"]")
        print("String is accepted.")
    else:
        print("["+str(cur_state)+"]")
        print("String is not accepted.")

【讨论】:

【参考方案6】:

接受@John Coleman 的字符串 101* 和 001* 修改

#Dfa 只接受 101+00101001

dfa101 = 0:'1':1,
       1:'0':2,
       2:'1':3,
       3:'0':3, '1':3

#Dfa for accepting only 001+00101001
dfa001=0:'0':1,
       1:'0':2,
       2:'1':3,
       3:'0':3, '1':3



def accepts(transitions,initial,accepting,s):
    state = initial
    try:
        for c in s:
            state = transitions[state][c]
        if(state in accepting):
            return 'Accepted'
        else:
            return 'Rejected'
    except:
        return 'Rejected'
print('Dfa of 101+ ',accepts(dfa101,0,3,'10101111000')) #Accepted

print('Dfa of 001+ ',accepts(dfa001,0,3,'00101010101')) #Accepted

【讨论】:

【参考方案7】:

为了展示我的解决方案,我们以下面的 DFA 为例:

在 ? = (0, 1) 上的语言包含仅由 1 组成的字符串或每个 0 后跟一个 1 的字符串。

此 DFA 的转换表是:

delta 0 1
->*S A S
A D S
D D D

我的程序是用 Python3 编写的,旨在接受任何 DFA 在字母表 (0, 1) 上的转换表,以检查字符串是否会被 DFA 接受。

要使用我的程序,我们必须将上面的转换表按以下格式输入到与程序位于同一目录下的名为 fa.txt 的文本文件中。

fa.txt:

->*s(a,s)
a(d,s)
d(d,d)

对于开始状态,状态名称必须以 -> 开头,最终状态必须以 * 开头。如果开始状态是最终状态,-> 必须在 * 之前。 州名的长度只能是一个字符。 起始状态必须命名为 s。 TXT 文件中的状态顺序无关紧要。

代码:

file = open("fa.txt", "r")
l = file.readlines()
x = 0

def findState(state):
    lines = l
    for i in range(0, len(lines)):
        if lines[i][0] == '-':
            lines[i] = lines[i][2::]
        if lines[i][0] == '*':
            if state == lines[i][1]:
                return [lines[i][3], lines[i][5], 1]
        if lines[i][0] == state:
            return [lines[i][2], lines[i][4], 0] # state to go to on 0, state to go to on 1, is it final?

s = input("Enter a binary string to test it against the DFA in file fa.txt: ")
on0_on1 = findState('s') # start state
print("s", end = "")

for c in range(0, len(s)):
    if s[c] != '0' and s[c] != '1':
        print("Fail")
        exit(0)
    if s[c] == '0':
        print(' ---0--->', on0_on1[0], end = '')
        on0_on1 = findState(on0_on1[0])
    else:
        print(' ---1--->', on0_on1[1], end = '')
        on0_on1 = findState(on0_on1[1])
    if on0_on1[2] == 1 and c == len(s) - 1:
        print("\nPass")
    elif c == len(s) - 1 and on0_on1[2] == 0:
        print("\nFail")

【讨论】:

以上是关于有限自动机是如何在代码中实现的?的主要内容,如果未能解决你的问题,请参考以下文章

全自动荧光免疫分析仪系统是如何在FET4418-C核心板中实现的

有限的个人资料,如 Facebook

PhoneGap:iOS 7 中的 UIPickerView 不会像在 iOS 6 中那样自动调整字体大小。关于如何在 iOS 7 中实现的任何解决方案?

词法分析器:通过有限自动机实现正则表达式?

有限状态自动机

图形绘制算法 - 我正在尝试渲染有限状态自动机