aquí - DSIC

Anuncio
Resolución del examen de teoría de Algoritmos y estructuras de Datos II (AD2).
Septiembre 2000.
b)
-
Problema 1: el siguiente algoritmo permite conocer para cada prefijo de un vector v de n
elementos (esto es, para cada subvector v[1..i], 1≤i≤n) si dicho prefijo es o no capicúa.
type Tvector=array[1..Nmax] of integer;
P={1≤n≤Nmax}
procedure prefijos_capicua (var v:Tvector; n:integer);
var i,j,mitad:integer; capicua:boolean;
begin
i:=1;
while i<=n do begin
j:=1;
mitad:=i div 2;
capicua:=true;
while (capicua) AND (j<=mitad) do
if v[j]= v[i-j+1]
then j:=j+1
else capicua:=false;
if capicua
then writeln(‘El prefijo de longitud’,i,’ es capicua’)
else writeln(‘El prefijo de longitud’,i,’ NO es capicua’);
i:=i+1;
end;
end;
Se pide: estudiar la complejidad temporal del algoritmo, determinando:
a) Talla del problema y análisis por casos, discutiendo las diferentes instancias si las
hubiere.
(1 punto)
b) Complejidad temporal asintótica, haciendo explícitos los cálculos necesarios para su
determinación.
(1.5 puntos)
Resolución:
a) Tomamos como tamaño del problema el número de elementos del vector tratados, n.
Aunque el bucle más exterior se realiza siempre n veces, independientemente de la
instancia, no ocurre lo mismo con el bucle que aparece en su interior, while (capicua)
AND (j<=mitad) do: en él se determina si cada cada prefijo de v, esto es, cada
subvector v[1..i], 1≤i≤n, es o no capicúa y por lo tanto, al tratarse de una búsqueda, se
ejecutará más o menos veces dependiendo de la instancia. En particular:
- El peor de sus casos se daría cuando bien el subvector que se está analizando es
capicúa, bien no es capicúa porque sólo su(s) elemento(s) central(es) son distintos.
- El mejor de los casos sería cuando el subvector que se está analizando no es capicúa
porque su primer y último elemento, v[1] y v[i] respectivamente, son distintos.
Como se comprobará en el apartado b) de este ejercicio, la complejidad del algoritmo varía
según la instancia del bucle de búsqueda, de forma que:
-
El peor de los casos del algoritmo es, también, el peor de los casos del bucle de
búsqueda y,
-
el mejor de los casos del algoritmo es, también, el mejor de los casos del bucle de
búsqueda.
Peor de los casos del algoritmo: si consideramos como instrucción significativa del
bucle de búsqueda la evaluación de la guarda de la condicional if v[j]= v[i-j+1]
tenemos:
n idiv2
n
n
n(n + 1)
t peor ( n ) = ∑ ∑1 = ∑ idiv2 ≈ ∑ i/2 =
∈ Θ(n 2 )
4
i =1 j =1
i =1
i =1
Con lo que tenemos que la complejidad del algoritmo es cuadrática en función del
número de elementos del vector tratados, n. Esto es:
t ( n ) ∈ Ο( n 2 )
-
Mejor de los casos del algoritmo: si consideramos como instrucción significativa del
bucle más exterior la evaluación de la guarda del bucle de búsqueda, (capicua) AND
(j<=mitad), tenemos:
n
t mejor ( n ) = ∑ 1 = n ∈ Θ( n )
i =1
Con lo que la complejidad del algoritmo es lineal en función del número de elementos
del vector tratados, n. Esto es:
t ( n ) ∈ Ω( n )
Problema 2: dado un conjunto de caracteres se desea saber si un determinado carácter
pertenece o no al mismo. Suponiendo que el conjunto de caracteres está representado sobre
un vector v de tamaño n, con n>0 y sin elementos repetidos, se desea diseñar una función
recursiva busca_car que dado el vector v y un carácter ch devuelva:
- la posición del carácter ch en el vector v, si éste estuviera,
- el valor 0 si ch no estuviera en v.
Ejemplo: sea v el siguiente vector de tamaño n=4.
v
a l
b o ..................
1 2 3 4=n
Nmax
Si ch=’b’ entonces busca_car devuelve 3; sin embargo, si ch=’m’ entonces devuelve 0.
Se pide:
a) Cabecera de la función busca_car (o perfil de la operación), análisis por casos, función
limitadora, instrucciones del algoritmo y llamada inicial.
(2.5 puntos)
b) Estudio del coste temporal del algoritmo, describiendo las distintas instancias
significativas si las hubiere, y planteando y resolviendo las ecuaciones de recurrencia
correspondientes.
(1 punto)
Resolución:
a) Cabecera de la función:
función busca_car (v:TipoVector; n,i:entero; ch:carácter) devuelve entero;
donde el parámetro i determina la primera posición del vector sobre el que se aplica,en
cada llamada, la función. Así, 1≤i≤n≤Nmax.
Análisis por casos:
Caso base: i = n,
Caso general: i<n
Función limitadora:
t=n-i+1, que es estrictamente decreciente, positiva y acotada, que
corresponde al número de elementos del subvector en que se busca el carácter ch.
Instrucciones del algoritmo:
Tipo TipoVector = vector[1..Nmax] de carácter;
{1≤i≤n≤Nmax}
función busca_car (v:TipoVector; n,i:entero; ch:carácter) devuelve entero;
opción
i=n: si v[i]=ch entonces pos:=i sino pos:=0;
i<n: opción
v[i]=ch: pos:=i;
v[i]<>ch: pos:= busca_car (v,n,i+1,ch);
fopción
fopción
devuelve pos;
ffunción;
Llamada inicial: busca_car (v,n,1,ch)
b) Al ser una función recursiva de búsqueda, su complejidad varía en función de la
instancia de forma que:
- El peor de sus casos se daría cuando bien el carácter ch no aparece en v, o bien
aparece en la posición n.
- El mejor de los casos se daría cuando el carácter ch aparece en la posición 1 de v.
Por tanto, si el tamaño del problema es el número de elementos del vector sobre los que se
busca ch, talla=n-i+1:
-
Peor de los casos del algoritmo:
t peor (talla − 1) + k1
si talla > 1

peor
t (talla ) = 
 k
si talla = 1
 2
- Mejor de los casos del algoritmo:
t mejor (talla ) = k 3 ∈ Θ(1)
Por tanto, para la llamada inicial propuesta, en la que i=1 y por tanto talla=n, y
considerando que el vector se pasa por referencia:
t peor (n − 1) + k1
si n − 1 > 1

peor
t (n) = 
⇒ t peor ( n ) ∈ Θ( n ) ⇒ t (n ) ∈ Ο( n )
 k
sino
 2
t mejor ( n ) = k 3 ∈ Θ(1) ⇒ t ( n ) ∈ Ω(1)
Problema 3: sea l una lista con punto de interés de números enteros, potencialmente vacía.
Se desea modificar el valor de los elementos de la lista l de forma que cada elemento pase
a valer el triple de su valor original.
Ejemplo: si inicialmente l = 3,121,-6,5,4,5,22,-1 entonces, tras la modificación
requerida l = 9,363,-18,15,12,15,66,-3
Se pide: diseñar un algoritmo para que dada una lista con punto de interés l de números
enteros, potencialmente vacía, modifique los elementos de la misma para que pasen a valer
el triple de su valor original. Dicho algoritmo debe actuar sobre la lista original sin hacer
uso de otra lista distinta.
(2.5 puntos)
Resolución: como no existe una operación explícita en las listas con punto de interés para
modificar el valor que tiene el elemento en la posición distinguida, es necesario, para cada
elemento, bien eliminar el original insertándolo después, bien insertar el nuevo elemento
para, a continuación, eliminar el antiguo. Nótese que los estados intermedios en la
aplicación de las dos estrategias son distintos, pero el estado final es el mismo.
Obviamente, es necesario aplicar una de las dos estrategias mencionadas a todos los
elementos de la lista.
La siguiente resolución aplica la segunda de las estrategias comentadas:
{l=L1 L2 .. Ln-1 Ln}
procedimiento triplicar_valor(e/s l:lista de enteros);
principio(l);
mientras not(esfin(l)) hacer
inserta(l,3*recupera(l));
{el punto de interés sigue en el elemento original a borrar}
{y el elemento anterior, insertado, es triple del original }
suprime(l);
{el elemento original se ha borrado y el punto de interés }
{está en el siguiente elemento a cambiar}
fmientras
fprocedimiento
{l=3*L1 3*L2 .. 3*Ln-1 3*Ln}
Nótese que es incorrecto avanzar utilizando la operación siguiente, ya que al suprimir un
elemento el punto de interés se desplaza al elemento siguiente al eliminado.
Problema 4: para la implementación enlazada con variable dinámica del tipo Pila de
Elemento vista en clase, se efectuaba una definición de tipos similar a la siguiente:
Tipo
enlace = ^nodo;
nodo = tupla
dato: Elemento; prox: enlace;
ftupla;
pila = enlace;
fTipo;
Se pide: dada la declaración anterior implementar una nueva operación para enriquecer el
tipo Pila de Elemento, denominada TopeBase cuya sintaxis será:
procedimiento TopeBase (ent/sal p:pila);
para intercambiar el valor del elemento en la base de la pila con el del tope de la misma, y
cuya semántica será:
{p=P1 P2 .. Pn-1 Pn ∧ p≠λ}
TopeBase(p)
{p= Pn P2 .. Pn-1 P1 }
(1.5 puntos)
Resolución: nótese que la precondición establece que la pila no está inicialmente vacía. De
lo contrario, sería necesario determinarlo inicialmente.
procedimiento TopeBase (ent/sal p:pila);
var q:enlace; e:elemento fvar
q:=p;
e:=q^.dato;
{e contiene el valor del elemento tope de la pila}
mientras q^.prox <> nil hacer
{en cada iteración q apunta a un nodo cuyo campo prox no es nil}
q:=q^.prox;
fmientras;
{q apunta al último nodo de la lista (primer nodo con campo prox a nil)}
p^.dato:=q^.dato;
q^.dato:=e;
{los valores del tope y la base se han intercambiado}
fprocedimiento
TIPUS llista[element]
SINTAXI
procediment creal (s l:llista);
procediment inserta (e/s l:llista; e:element);
procediment suprime (e/s l:llista);
función recupera (l:llista) torna element;
procediment principio (e/s l:llista);
procediment fin (e/s l:llista);
procediment siguiente (l:llista);
funció esvacial (l:llista) torna lògic;
funció esfinl (l:llista) torna lògic;
SEMÀNTICA
Notació emprada: l = L1L2..Ln ∧ 0≤n, on n=0 equival a l=λ,
On pos, 1≤pos≤n+1,és la posició del punt d’interés.
{}
creal(l)
{l =λ ∧ pos=1}
{l =L1L2..Ln ∧ pos=i ∧ 1≤pos≤n+1 ∧ e=E }
inserta(l,e)
{l=L’1L’2...L’n+1∧(L’1=L1...L’i-1=Li-1)∧L’i=E∧(L’i+1=Li...L’n+1=Ln)∧pos=i+1}
{l =L1L2...Ln ∧ pos=i ∧ 1≤pos≤n }
suprime(l)
{l=L’1 L’2...L’n-1 ∧(L’1=L1...L’i-1= Li-1)∧(L’i=Li+1...L’n-1= Ln)∧ pos=i }
{l = L1L2...Ln ∧ 1≤pos≤n }
e:=recupera(l)
{l = L1L2...Ln ∧ e=Lpos}
{l=L1 L2...Ln ∧ 0≤n}
principio(l)
{l=L1L2...Ln ∧ pos=1}
{l=L1L2...Ln ∧ 0≤n}
fin(l)
{l=L1L2...Ln ∧ pos=n+1}
{l=L1L2...Ln ∧ 1≤pos≤n ∧ pos=i}
siguiente(l)
{ l=L1L2...Ln ∧ pos=i+1}
{l=L1L2...Ln ∧ 0≤n}
b:=esvacial(l)
{b=(n=0)}
{l=L1L2...Ln ∧ 0≤n}
b:=esfin(l)
{b=(pos=n+1)}
Descargar