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?
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
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.
eh bien, je présente ici une solution récursive pour NFA.
considérons le nfa suivant:
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
......
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()
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