Está en la página 1de 24

Evidencia de desempeño

unidad 2

Nombre: Briseldy Berenice Navarro Mata


Matriculas: 362122347
Clase: 29 A ISC nocturno
Materia: lenguajes y autómatas
Catedrático: Ángel Barrios
Resumen
En el siguiente documento se muestra las diferencias entre lenguajes formales e
informales y como se pueden definir cada uno de ellos, además de los elementos
que le caracterizan a cada uno.
Además de lo anterior se hará referencia a las definiciones y características de los
lenguajes autómatas deterministas y no deterministas, además de mostrar las claras
diferencias entre ambos contextos, así mismo también las conversiones entre
ambos y por último se desarrollará un algoritmo que nos ayude a hacer estas
equivalencias de manera tecnológica por medio del lenguaje de programación C++.
Introducción
El estudio de la teoría de autómatas y de los lenguajes formales se puede ubicar en
el campo científico de la Informática Teórica, un campo clásico y multidisciplinar
dentro de los estudios universitarios de Informática. Es un campo clásico debido no
sólo a su antigüedad (anterior a la construcción de los primeros ordenadores) sino,
sobre todo, a que sus contenidos principales no dependen de los rápidos avances
tecnológicos que han hecho que otras ramas de la Informática deban adaptarse a
los nuevos tiempos a un ritmo vertiginoso. Es multidisciplinar porque en sus
cimientos encontramos campos tan aparentemente dispares como la lingüística, las
matemáticas o la electrónica.
El estudio de las máquinas secuenciales que abarca la teoría de autómatas, por una
parte, sienta las bases de la algoritmia y permite modelar y diseñar soluciones para
un gran número de problemas. Por otra parte, permite abordar cuestiones de gran
interés para un informático como qué tipo de problemas pueden ser resueltos por
un computador o, caso de existir una solución computable para un problema, cómo
podemos medir la calidad (en términos de eficacia) de dicha solución. Es decir, la
teoría de autómatas es la puerta que nos permite la entrada hacia campos tan
interesantes como la computabilidad y la complejidad algorítmica. Además, una de
las principales aportaciones del estudio de los lenguajes formales, sobre todo desde
un punto de vista práctico, es su contribución al diseño de lenguajes de
programación y a la construcción de sus correspondientes traductores. En este
sentido la asignatura ayudará a conocer con mayor profundidad la estructura de los
lenguajes de programación y el funcionamiento de los compiladores.
Desarrollo.
1.- Dictamen de un lenguaje formal e informal
Lenguajes formales
Un lenguaje formal es un conjunto (finito o infinito) de cadenas finitas de símbolos
primitivos Ej: El lenguaje “Número” es simplemente el conjunto infinito de cadenas
finitas formadas con los dígitos 0, 1, 2, 3, 4, 5, 6, 7, 8 y 9 Dichas cadenas están
formadas gracias a un alfabeto y a una gramática que están formalmente
especificados El alfabeto es un conjunto finito no vacío de símbolos La gramática
es un conjunto finito de reglas para formar cadenas finitas juntando símbolos del
alfabeto A cada cadena de símbolos de un lenguaje formal se le llama fórmula bien
formada (o palabra) del lenguaje
Clasificación de gramáticas formales Chomsky clasificó jerárquicamente las
gramáticas formales que generan lenguajes formales, en estos tipos: Tipo 3:
Gramáticas regulares que generan lenguajes regulares Tipo 2: Gramáticas
incontextuales que generan lenguajes incontextuales Tipo 1: Gramáticas
contextuales que generan lenguajes contextuales Tipo 0: Gramáticas libres que
generan lenguajes sin ningún tipo de restricción Cuanto menor es el tipo, mayor es
el poder expresivo del lenguaje generado y más complejidad tiene su tratamiento
por parte de una máquina
Lenguajes regulares
Un lenguaje regular es un lenguaje formal que tiene estas características:
Puede ser descrito mediante una expresión regular (expresar de forma compacta
cómo son todas las cadenas de símbolos que le pertenecen) Puede ser generado
mediante una gramática regular (obtener todas las cadenas de símbolos que le
pertenecen) Puede ser reconocido mediante un autómata finito (saber si una
cadena de símbolos pertenece a él o no) ¡Todas estas características facilitan
mucho su tratamiento computacional, por eso nos interesan los lenguajes regulares!
El conjunto de expresiones regulares sobre un alfabeto A se denomina ER(A) y sólo
contiene expresiones formadas mediante estas reglas:
∅ ∈ ER(A) y denota el lenguaje {}, siendo ∅ el vacío λ ∈ ER(A) y denota el lenguaje
{λ}, siendo λ la cadena vacía Si x ∈ A, x ∈ ER(A) y denota el lenguaje {x}
Si H∈ ER(A) y K ∈ ER(A), con lenguajes denotados LH y LK (H | K) ∈ ER(A) y denota
el lenguaje LH ∪ LK (Conjunto de todas las cadenas de H o de K) (HK) ∈ ER(A) y
denota el lenguaje LHK siendo LHK = {hk tal que h ∈ LH y k ∈ LK} (Conjunto de
todas las concatenaciones posibles de una cadena de H y otra de K) H* ∈ ER(A) y
denota el lenguaje LH* siendo LH* = {λ} ∪ {aα tal que a ∈ LH y α ∈ LH*} (Conjunto
de todas las concatenaciones sucesivas posibles de cadenas de H) Los paréntesis
() asocian operadores a cadenas de símbolos. Si no aparecen, repetir * es más
prioritario que concatenar y concatenar más prioritario que alternar |
Extensiones a la notación de las expresiones regulares
Estas extensiones no amplían la expresividad, pero hacen mucho más cómodo
expresar lenguajes con ERs H? ≡ H | λ (Se llama opcionalidad y tiene la misma
prioridad que repetición *) H+ ≡ H(H)* (Se llama cierre positivo + y tiene la misma
prioridad que repetición *) [xn - xm] ≡ xn | xn+1 | xn+2 | … | xm (Se llama rango [ -
] y sólo se usa para alfabetos totalmente ordenados)
Ej: Intervalos de números naturales como [3-9] o de caracteres ASCII como [f-m] \x
≡ carácter x (Se llama escape de metacaracteres \ y se usa para incluir en el
lenguaje definido caracteres que actúan como operadores, es decir son
metacaracteres, en las expresiones regulares)
Ej: \* para denotar el carácter *
Ej: \\ para denotar el carácter \ …
2.- Grafo del autómata finito determinista (AFD)
Este tipo de autómatas admite su definición de dos maneras bien diferentes:

Como autómatas traductores o reconocedores. La definición como autómatas


traductores continua a la definición de las máquinas secuenciales, y se los podría
definir como una subclase de estas, ya que los autómatas finitos tendrían como
limitante no poder iniciar desde cualquier estado como lo hacen en las máquinas
secuenciales.

La forma que adoptaremos para la definición de los autómatas finitos deterministas


es como autómatas reconocedores, ya que se ajusta con los contenidos de la
informática teórica y utilización que se les da dentro del diseño de los analizadores
léxicos.

Estos autómatas solo se limitarán a aceptar o no una determinada cadena recibida


en la entrada, por lo tanto, podemos decir que la salida de los mismos solo tendrá
dos valores posibles aceptar o no aceptar a la palabra de entrada.

Al igual que en las máquinas secuenciales, estos autómatas transitarán entre un


conjunto finito de estados posibles, a medida que reciban sucesivamente los
caracteres de entrada, en un instante determinado de tiempo el autómata solo podrá
estar en uno y solo uno de los estados posibles.
Una característica importante de este tipo de autómatas es el determinismo, lo cuál
significa que estando en un estado y recibiendo una entrada del exterior el autómata
tendrá la posibilidad de transitar a uno y solo un estado del conjunto de estados
posibles.

Con respecto al conjunto de estados ahora se pueden clasificar en tres


tipos: Estado Inicial, que es pon donde comenzará la ejecución de la
máquina; Estados finales o de aceptación que será un subconjunto del conjunto
de estados por los que transitará la máquina, y si cuando se hayan terminado de
procesar todos los símbolos de entrada y no reste ningún símbolo por leer, la
máquina quede posicionada en uno de estos estados de aceptación, se concluirá
que la cadena procesada será aceptada por el autómata. y Estados Intermedios,
que tienen comportamiento idéntico a los definidos en las máquinas secuenciales.

Los autómatas finitos deterministas quedarán formalmente definidos mediante una


quíntupla como sigue:

AFD = ( ∑ , Q, q0, F, f )

donde:

Interpretación de funcionamiento:
Este autómata recibirá una cadena en la cinta de entrada e ira procesando de uno
a la vez los símbolos de entrada. Comenzará su funcionamiento posicionado en el
estado inicial, y desde este estado comenzará su ejecución.

En cada intervalo de tiempo discreto realizará dos acciones las cuales serán
consideradas como acciones indivisibles en un determinado intervalo de tiempo.

Las acciones que realiza son:

• Leer el próximo símbolo, desde la cinta de entrada.


• Transitar, cambiar de estado
Extensión a palabras

El autómata finito determinista realizará transiciones de estados a través de la


función f solo cuando reciba un símbolo de entrada. Esto puede generalizarse a una
palabra completa, o cuando reciba la palabra vacía, en este caso se denominará
una función de transición f´ como la función

f´ : Q x ∑* Q

Donde:

f´ (q, ax) = f´(f(q,a),x)

f´ (q, ) = q con a ∑;x ∑* ; q Q

Aceptación de Palabras:

Una palabra será aceptada por un AFD si estando formada pos símbolos
pertenecientes al alfabeto de entrada, al finalizar de procesar la misma, el autómata
queda posicionado en una de los estados perteneciente al conjunto de estados
finales de aceptación.

Dada:
x W(∑)

f ( q0, x) = qn Si qn F la cadena x es aceptada

Lenguaje reconocido por un AFD:

Es el conjunto de todas las palabras reconocidas por el Autómata Finito


Determinista.

L AFD = { x / x ∑* y f ( q0, x) = qn con qn F}

Ejemplo:

Dado el siguiente AFD definido como:


AFD1 =(∑,Q,qi,F,f),donde:

∑= { 0,1 } (Conjunto de símbolos de entrada del AFD)

Q= { p, q, r } (Conjunto de estados del AFD)

qi = p (estado inicial del AFD (p Є Q))

F= {q } (Conjunto de estados de aceptación del AFD ({q } с Q))


f: función de transición de estados del AFD

Donde la función f será:

Representando su comportamiento por medio del grafo dirigido:

En donde el lenguaje aceptado por esta AFD será:

L AFD1 = {x / x ∑* y x = 0.1n con n N 0}

3.- Grafo del autómata finito no determinista (AFN) y su equivalente en AFD.


Este tipo de autómatas se diferencia de los AFD, básicamente en cómo puede
constituirse la función de transición:

Estas diferencias son las siguientes:

1) Para cada par estado entrada, el autómata puede


tener la posibilidad de transitar a mas de un estado
posible.
2) Para algún par de estado entrada, el autómata
puede no tener definido ninguna transición. Lo que
significa que podrá realizar transición alguna.
3) Puede realizar transiciones de un estado a otro sin
leer símbolo alguno de la entrada. A este tipo
particular de transiciones se las denomina
transiciones- .
La equivalencia entre autómatas AFD y AFND, siempre es posible, y esto significa
que siempre se podrá dado uno de ellos obtener su equivalente. Por lo tanto, se
podrá asegurar que ambos, AFD y AFND reconocerán el mismo lenguaje.

Conversión de AFD a AFND :

Es fácil ver que los AFD son una particularización de los AFND, ya que: por un lado
no existirán transiciones-λ y por otro lado, las transiciones siempre producirán un
estado determinado. Así tendremos que:

• No existirán transiciones-λ por lo tanto: f” (q, λ) = ф.


• | f (p,a)| = 1 ; Siempre se transita a un solo estado

Conversión de AFND a AFD:

· Vamos a construir un AFD partiendo de un AFND, donde:


· AFND = ( ∑ , Q, q0, F, f )
· AFD = ( ∑ , Q´, q0´, F ´, f ´ )
· donde para la conversión se tendrá en cuenta:
· Q´ = P(Q)
· q0´ = f” (q0, λ) ; El nuevo estado inicial, se obtiene en forma directa
de T*.
· F´ = { c / c ε Q´ Si existe q ε c y q ε F } Un estado perteneciente
al nuevo conjunto de estados será de finalización o aceptación, solo
si esta constituido por estados que eran de aceptación en el
autómata en el AFND de origen.
· f´(c,a) = { c / c ε Q´ = ∪ q c f ” (q,a) .
Lo que se hará primero es calcular q0´ y luego se irán calculando los demás
estados Q´ obtenidos a partir de P(Q) a medida que van presentando en el proceso
de conversión.

Ejemplo:

Dado:

AFND1 = ( { 0 , 1 }, { p, q , r, s , t }, p, { r }, f ) , donde:

Obtendremos ahora el AFD equivalente.

T = {(p,t) , (t,s) , (p,p) , (q,q) , (r,r) , (s,s) , (t,t) }

T* = {(p,t) , (p,s) , (p,p) , (q,q) , (r,r) , (s,s) , (t,t), (t,s) }

Nuevo estado inicial c0 = f “ ( p, ) ; lo obtendremos de T*

c0 = { t , s , p}

Para el estado t c0 :
Para el estado s ε c0 :

Para el estado t ε c0 :
De esta manera:

f´( c0 , 0 ) = { ∪ (ⓐ, ⓑ, ⓒ) }

Por lo tanto: f ´ (c0 , 0 ) = { t, s, p, r } = c1

De igual manera calcularemos a c0 pero ahora con entrada 1, pero simplificando la


representación de los pasos y nos queda:

Ahora debemos para cada uno de los estados que vaya apareciendo, como se va
comportando ante ambas entradas. Tomamos ahora el estado c1 = { t,s,p,r }
Ahora tomamos c2 = { t, s}, y vemos como se comportará la función de transición f
´ ante nuevas entradas 0 y 1, respectivamente.

Vemos ahora que no se ha creado ningún nuevo estado, por lo tanto hemos
terminado el proceso de construcción de la función f ´ y del conjunto Q ´.

Nuevo P(Q)
estado Q´
c0 { t,s,p }
c1 { t, s, p, r}
c2 {s,t}
Ahora estamos en condición de formalizar el AFD equivalente al AFND

AFD = ( { 0 , 1 }, { c0 , c1, c2 }, c0 , { c1 }, f ´) , donde:


Los estados finales de aceptación en el ADND, es el conjunto F = { r }, por lo tanto
F ´ estará conformada por todos los nuevos estados que incluyan a dicho estado.
Entonces el estado c1 será estado de aceptación del nuevo autómata.

Donde la función de transición será:

Y el grafo del AFD quedará:

4.- Código fuente del algoritmo de conversión entre una expresión regular y
AFD
El programa AFNAFD.CPP se ha escrito en lenguaje C++, usando el compilador
Turbo C/C++ 3.0 para MS-DOS. Se emplea la estructura de datos Pila. El símbolo
(epsilon) se define a -1 como una constante simbólica. La matriz bidimensional de
estados del AFN “N” se inicializa dentro del programa. El código del programa se
muestra en su totalidad en las subsiguientes figuras.
#include <iostream.h>
#include <iomanip.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#define AFN_MAX 11
#define EPSILON -1
#define ACEPTA -2
#define STACKSIZE 100
#define MAXIMO 80
enum bool {FALSE,TRUE};
struct stack {
int top;
int items[STACKSIZE];
};
typedef stack *STACKPTR;
bool empty(STACKPTR ps);
int pop(STACKPTR ps);
void push(STACKPTR ps,int x);
void init(STACKPTR ps);
void e_clausura(int* ,int,int* ,int &na);
void mover(int *,int,int* t,int &m,int c);
void print(int* t,int m,char c);
void copy(int*,int,int estadosD[][AFN_MAX],int &nest,int*);
bool no_marcado(int*,int,int estadosD[AFN_MAX][AFN_MAX],int nest);
void aceptacion();
void print_estadosD(int estadosD[][AFN_MAX],int nest,int nD[]);
// 0 1 2 3 4 5 6 7 8 9 10
int aristas[][AFN_MAX]=
{0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0,
0, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0,
0, 0, 0,'a', 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0,
0, 0, 0, 0, 0,'b', 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0,
0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,'a', 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,'b', 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'b',
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2 };
void main()
{
int m,i,j,k; int t[AFN_MAX];
int estadosD[AFN_MAX][AFN_MAX],nest=0;
int nD[AFN_MAX];
int a[AFN_MAX],na;
char* alfabeto="ab";
clrscr();
*t=0;m=1;
cout << "estado : " << nest << endl;
e_clausura(t,m,a,na);
copy(a,na,estadosD,nest,nD);
for(i=0;i<strlen(alfabeto);i++){
mover(a,na.t.m.alfabeto[i]);
cout<< "estado : " << nest << endl;
print(t,m,alfabeto[i]);
copy(t,m,estadosD,nest,nD);
}
for(k=1;k<nest;k++){
for(j=0:j<nD[k];j++){
t[j]=estados[k][j];
m=nD[k]:cout <<”K:” << k << endl;
e_clausura(t,m,a,na);
for(i=0;i<strlen(alfabeto);i++){
mover(a,na,t,m,alfabeto[i]);
if(m){
if(no_marcado(t,m,estadosD,nest)){
cout<<”estado: " << nest << endl;
copy(t,m,estadosD,nest,nD);
print(t,m,alfabeto[i]);
}
else
print(t,m,alfabeto[i]);
}
}
}
aceptacion();
print_estadosD(estadosD,nest,nD);
}
void print_estadosD(int estadosD[][AFN_MAX],int nest,int nD[])
{
register int i,j;

clrscr();
cout << " AFD AFN " << endl;
cout << "---------------------------------------" << endl;
for(i=0;i<nest;i++){
cout<< setw(4) << i << " : ";
for(j=0;j<nD[i];j++){
cout<< setw(4) << estadosD[i][j];
cout << endl;
}
cout << "---------------------------------------" << endl;
getch();
}
void aceptacion()
{
cout << "estados de aceptacion tienen los nodos : ";
for(int i=0;i<AFN_MAX:i++){
if(aristas[i][i]==ACEPTA){
cout<< setw(4) << i;
getch();
}
}
void e_clausura(int* s,int n,int* a,int &na)
{
int i,j,t,u;
STACKPTR p;
// meter todos los estados en pila
// inicializar cerradura a
init(p);
na=0;
for(i=0;i<n;i++){
push(p,s[i]);
a[i]=s[i];
na++;
}
While(!empty(p)){
T=pop(p);
for(u=0;u<AFN_MAX:u++){
if(aristas[t][u]==EPSILON){
i=0;
while(i<na&&u!=a[i]){
i++;
if(i==na){
a[na++]=u;
push(p,u);
}
}
}
}
}
cout<< " T :";
for(j=0;j<na;j++){
cout<< setw(4) << a[j];
cout << endl;
}
void print(int* t,int m,char c)
{
register int j;
cout << " mover(T," << c << ") : ";
for(j=0;j<m;j++){
cout<< setw(4) << t[j];
cout << endl;
}
void copy(int* t,int m,int estadosD[][AFN_MAX],int &nest,int* nD)
{
register int i;
for(i=0;i<m;i++){
estadosD[nest][i]=t[i];
nD[nest]=m;
++nest;
}
}
Void mover(int* a,int na, int* t, int &m,int c)
{
int i,j,k;
m=0;
for(i=0;i<na;i++){
k=a[i];
for(j=0;j<AFN_MAX;j++){
if(aristas[k][j]==c){
t[m++]=j;
}
}
bool no_marcado(int* t, int m, int estadosD[][AFN_MAX],int nest)
{
Int k=0,j,i;
For(k=0;k<nest;k++){
I=0;
For(j=0;j<m;j++){
If(t[j]==estados[k][j]){
I++;
If(i==m){
Return FALSE;
}
}
}
Return TRUE;
}
Bool empty(STACKPTR ps)
{
If(ps-<itop==-1){
return TRUE;
}
Else{
return FALSE;
}
}
int pop(STACKPTR ps)
{
if(empty(ps)) {
cout << "\n pila vacia " << endl;
exit(1);
}
return (ps->items[ps->top--]);
}
void push(STACKPTR ps,int x)
{
if(ps->top==STACKSIZE-1) {
cout << "\n stack overflow" << endl;
exit(1);
}
else {
ps->items[++(ps->top)]=x;
}
}
void init(STACKPTR ps)
{
ps->top=-1;
}
Conclusiones
Este algoritmo conocido como el de construcción de subconjuntos convierte un AFN
en un AFD haciendo uso de tres operaciones sobre los estados de un AFN y una
pila. Lo importante del algoritmo es que se procesa el conjunto de estados del AFN
denominado “T”, usando operaciones de una estructura llamada pila.
El algoritmo sistemáticamente irá encontrando los nuevos estados de AFD usando
únicamente los símbolos de entrada. Si se desea correr el programa con un AFN
diferente se debe inicializar la matriz de estados dentro del programa.
Esto es conveniente cuando el AFN a convertir tiene un número de estados mayor
de 5; pues de lo contrario se tendrían que ingresar estos por teclado lo cual aumenta
la probabilidad que se introduzcan datos incorrectos.
Otra alternativa cuando la entrada es muy grande es guardar los datos de entrada
en un archivo para luego ser tomados por el programa.

También podría gustarte