it-roy-ru.com

Как конечные автоматы реализованы в коде?

Как реализовать dfa или nfa по этому вопросу в коде Python? 

Какие есть хорошие способы сделать это в Python? И используются ли они когда-либо в реальных проектах?

11
user5899005

Простой способ представления DFA - словарь словарей. Для каждого состояния создайте словарь, в котором используются буквы алфавита, а затем глобальный словарь, в котором указаны состояния. Например, следующий DFA из статьи Википедии о DFAs

 enter image description here

может быть представлен словарь, как это:

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, чтобы выбрать следующее состояние из набора возможных состояний.

20
John Coleman

хорошо, здесь я представляю рекурсивное решение для NFA.

рассмотрим следующее нфа:

 enter image description here

переходы могут быть представлены с использованием списка списков следующим образом:

переход = [[[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

......

2
user8038009

Вам не нужен цикл for over 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()
1
Shatrunjay Pathare

Вот моя версия реализации 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 __== '__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
1
Chihab