web-dev-qa-db-fra.com

Comment les automates finis sont-ils implémentés dans le code?

Comment peut-on implémenter une dfa ou une nfa dans le code Python?

Quels sont les bons moyens de le faire en python? Et sont-ils jamais utilisés dans des projets du monde réel?

11
user5899005

Un moyen simple de représenter un DFA consiste à utiliser un dictionnaire de dictionnaires. Pour chaque état, créez un dictionnaire avec les lettres de l'alphabet, puis un dictionnaire global avec les états. Par exemple, le DFA suivant de l’article de Wikipedia sur les DFA

 enter image description here

peut être représenté par un dictionnaire comme celui-ci:

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

Pour "exécuter" une dfa sur une chaîne d'entrée tirée de l'alphabet en question (après avoir spécifié l'état initial et l'ensemble des valeurs acceptantes), la procédure est simple:

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

Vous commencez dans l'état initial, parcourez la chaîne caractère par caractère et, à chaque étape, il vous suffit de rechercher l'état suivant. Lorsque vous avez fini de parcourir la chaîne, il vous suffit de vérifier si l’état final est dans l’ensemble des états acceptants.

Par exemple

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

Pour les NFA, vous pouvez stocker des ensembles d'états possibles plutôt que des états individuels dans les dictionnaires de transition et utiliser le module random pour sélectionner l'état suivant dans l'ensemble des états possibles.

20
John Coleman

eh bien, je présente ici une solution récursive pour NFA.

considérons le nfa suivant:

 enter image description here

les transitions peuvent être représentées à l'aide d'une liste de listes comme suit:

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

Remarque: L'état 4 est un état hypothétique. Une fois que vous allez dans cet état, vous ne pouvez pas aller plus loin. C'est utile lorsque vous ne pouvez pas lire l'entrée de l'état actuel. Vous allez directement à l'état 4 et dites que l'entrée n'est pas acceptée pour la progression actuelle (vérifiez les autres possibilités en remontant). Par exemple, si vous êtes à q1 et que le symbole de saisie actuel est 'a', vous passez à l'état 4 et arrêtez de continuer vos calculs.

voici le code 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()

exemple de sortie: (les chaînes se terminant par abb sont acceptées)

enter the string: abb
accepted

enter the string: aaaabbbb
rejected

......

2
user8038009

Vous n'avez pas besoin d'une boucle for over (len (entrée)) si vous utilisez la récursivité. Vous compliquez trop le code. Voici une version simplifiée

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

Voici ma version d'une implémentation de dfa si vous recherchez une implémentation plus orientée objet. La réponse de John Coleman m'a toutefois légèrement inspirée.

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