Les notebooks du répertoire documents
sont maintenant en lecture seule :
Shift-Entrée
ou Ctrl-Entrée
)Fichier > Créer une copie...
pour créer une copie du notebook dans votre répertoire (mais pas dans documents
).Noyau > Redémarer
... not defined
, unbound value ...
)N'hésitez pas à me poser des questions en cas de problème !
from automates import Automate
def afficher(automate):
import graphviz # pas à connaître du tout pour les concours ;-)
alpha, etats, I, F, delta = automate
s = ["digraph Automate {", "rankdir=LR", "node[shape=circle]"]
for e in etats:
if e in I:
s.append('"start_{}" [style=invis]'.format(e))
s.append('"start_{}" -> "{}"'.format(e, e))
s.append('"{}" [label="{}"{}]'.format(e, e, " penwidth=4" if e in F else ""))
for depart, lettre, arrivee in delta:
s.append('"{}" -> "{}" [label="{}"]'.format(depart, arrivee, lettre))
s.append("}")
return graphviz.Source('\n'.join(s), engine='dot')
def union_listes(u, v):
return sorted(set(u)|set(v))
def intersection_listes(u, v):
return sorted(set(u)&set(v))
def unique_liste(liste):
return sorted(set(liste))
A = Automate.glushkov("(ab(ab)*)*+(ba)*").export() # Wikipedia
B = Automate.glushkov("a(a+b)*a+b(a+b)*b").export() # Mines 2011
C = Automate.glushkov("a*bc*").export()
D = Automate.glushkov("aa*a").export()
A # (alphabet, états, états_initiaux, états_finaux, transitions)
Expression régulière linéarisée / marquage : $(a_0b_1(a_2b_3)*)*+(b_4a_5)*$
afficher(A)
def transition(automate, etats_depart, lettre):
""" On passe d'un ensemble d'états dans un ensemble d'états """
_, etats, _, _, delta = automate
assert all([e in etats for e in etats_depart]), ("État de départ inconnu : ", etats_depart, etats)
return unique_liste([y for (x, l, y) in delta for e in etats_depart if x==e and l==lettre])
transition(A, ['0', 'a_0', 'b_1'], 'a')
def reconnaitre(automate, mot):
_, _, I, F, delta = automate
etats = I
for lettre in mot:
etats = transition(automate, etats, lettre)
return intersection_listes(etats, F) != []
reconnaitre(A, "abab"), reconnaitre(A, "ababb")
def renommer(automate, prefixe=""):
alpha, etats, I, F, delta = automate
t = {}
for i, e in enumerate(etats):
t[str(e)] = prefixe+str(i)
return ([a for a in alpha],
[t[str(e)] for e in etats],
[t[str(e)] for e in I],
[t[str(e)] for e in F],
[(t[str(a)], l, t[str(b)]) for a, l, b in delta],
)
A_ok = renommer(A)
afficher(A_ok)
C_ok = renommer(C)
afficher(C_ok)
def union(A, B):
if intersection_listes(A[1], B[1]) != []:
A, B = renommer(A, "p"), renommer(B, "q")
assert intersection_listes(A[1], B[1]) == [], "Les états doivent être disjoints"
alphaA, etatsA, IA, FA, deltaA = A
alphaB, etatsB, IB, FB, deltaB = B
return (union_listes(alphaA, alphaB), etatsA+etatsB, IA+IB, FA+FB, deltaA+deltaB)
afficher(union(A, C)) # Ici, on obtient *un* automate non déterministe
nondeterministe = renommer(union(A, C))
afficher(nondeterministe)
def determiniser(A):
alphaA, etatsA, IA, FA, deltaA = A
alpha = alphaA[:]
etats, I, F, delta = [], [], [], []
I.append(IA) # Le (seul) nouvel état initial est l'ensemble des états initiaux de A
suivants = [IA] # parcours de graphe classique. Ici "vu" correspond à 'etats'
while suivants != []:
super_etat = suivants.pop() # chaque nouveau super_etat est une *partie* : ici une liste
if super_etat not in etats:
etats.append(super_etat)
if any(e in FA for e in super_etat):
F.append(super_etat)
for lettre in alpha:
arrivee = transition(A, super_etat, lettre) # automate de départ
delta.append((super_etat, lettre, arrivee))
suivants.append(arrivee)
return (alpha, etats, I, F, delta)
A_det = determiniser(A_ok)
afficher(A_det)
afficher(A_ok)
A_det = renommer(A_det)
afficher(A_det)
On ne veut garder que les états à la fois accessibles (depuis un état initial) et coaccessibles (à partir desquels on peut accéder à un état final)
def emonder(A):
alphaA, etatsA, IA, FA, deltaA = A
accessibles = []
suivants = IA[:] # on fait une copie pour ne pas modifier IA
while suivants != []:
etat = suivants.pop()
if etat not in accessibles: # C'est un parcours de graphe. `accessible` a le rôle de `vu`
accessibles.append(etat)
suivants.extend(unique_liste([y for x, _, y in deltaA if x==etat]))
coaccessibles = []
suivants = FA[:]
while suivants != []:
etat = suivants.pop()
if etat not in coaccessibles:
coaccessibles.append(etat)
suivants.extend(unique_liste([x for x, _, y in deltaA if y==etat]))
etats = intersection_listes(accessibles, coaccessibles)
delta = [(x, l, y) for (x, l, y) in deltaA if x in etats and y in etats]
return (alphaA[:], etats, intersection_listes(IA, etats), intersection_listes(FA, etats), delta)
A_em = emonder(A_det)
afficher(A_em)
afficher(A_ok) # Non déterministe
(etats, mot)
où etats
ets l'ensemble des états accessibles en lisant mot
["a", "b"]
, on condière d'abord le sommet correspondant au mot ""
, puis "a"
, "b"
, "aa"
, "ab"
, "ba"
, "bb"
, "aaa"
, "aab"
...def generer(automate, taille):
alpha, etats, I, F, delta = automate
mots = []
suivants = [(I, "")] # éléments : (ensemble d'état, mot lu)
while not (suivants==[] or len(mots)>=taille):
etats, mot = suivants.pop(0) # Parcours en largeur
if intersection_listes(etats, F) != []:
mots.append(mot)
for lettre in alpha:
suivants.append((transition(automate, etats, lettre), mot+lettre))
return mots
n = 8
generer(A, n), generer(A_det, n), generer(B, n), generer(C, n), generer(D, n),
def generer_mieux(automate, taille):
alpha, etats, I, F, delta = emonder(automate)
mots = []
suivants = [(I, "")] # éléments : (ensemble d'état, mot lu)
while not (suivants==[] or len(mots)>=taille):
etats, mot = suivants.pop(0) # Parcours en largeur
if intersection_listes(etats, F) != []:
mots.append(mot)
for lettre in alpha:
nouveaux_etats = transition(automate, etats, lettre)
if nouveaux_etats != []:
suivants.append((nouveaux_etats, mot+lettre))
return mots
E = Automate.glushkov("abcdefgh+(aaa)*").export()
generer_mieux(E, 30)
generer(E, 2)
def complet(A):
alpha, etats, I, F, delta = (x[:] for x in A)
existants = [(x, l) for (x, l, _) in delta]
non_existants = [(e, a) for e in etats for a in alpha if (e, a) not in existants]
if non_existants != []: # l'automate n'est pas complet
puits = "p"
if puits in etats:
i = 0
while "p"+str(i) in etats:
i+=1
puits = 'p'+str(i)
etats.append(puits)
for e, a in non_existants: # transitions manquantes
delta.append((e, a, puits))
for a in alpha: # boucles sur le puits
delta.append((puits, a, puits))
return (alpha, etats, I, F, delta)
afficher(complet(complet(A_ok)))
# TODO
# TODO
# TODO
# TODO
# TODO
# TODO
# TODO
# TODO