5.2. Etapas en el diseño de programas recursivos

Anuncio
Tema 5. Diseño de programas recursivos
5.1. INTRODUCCIÓN ............................................................................................................150
5.2. ETAPAS EN EL DISEÑO DE PROGRAMAS RECURSIVOS....................................159
5.3. EJEMPLOS DE PROGRAMAS RECURSIVOS ...........................................................164
EJERCICIOS ...........................................................................................................................170
150
Programación I
5.1. Introducción
 Un subprograma (procedimiento o función) es recursivo cuando en su definición aparece
una (o más) llamada a sí mismo.
- Números impares definidos en los naturales:
El número 1 es impar
El número 2 no es impar
Cualquier número N> 2 es impar si el número (N – 2) es impar
- Factorial de un entero:
0! = 1
n! = n * (n-1)!
- Definición de una oración:
Oracion  Sujeto Verbo
Sujeto  él
Verbo  come
Verbo  corre
Oracion  Oracion y Oración
Verbo  cose
 Definición de una sentencia:
Sentencia ::= if Condicion then Sentencia end if; | …
Tema5- Diseño de programas recursivos
Programación I
151
 El fundamento de la construcción de subprogramas recursivos consiste en suponer que ya
se dispone del propio subprograma que se quiere desarrollar, durante la definición de éste.
 “Principio de Inducción”
 Los casos triviales definen la solución directamente y los casos "más complicados" se
definen utilizando una (o más) llamadas al subprograma que se define.
Descomponer el problema en SUBproblemas
MISMO TIPO pero MENOR TAMAÑO

Tipo(PROBLEMA) == Tipo(SUBproblema)
Tema5- Diseño de programas recursivos
152
Programación I
Ejemplo 1: hacer un algoritmo que escriba todos los valores entre 1 y 10
Planteamiento iterativo:
procedure Escribir_Hasta_10
begin
for i in 1 .. 10 loop
Put (I)
end loop;
end;
Planteamiento recursivo: ahora vamos a tratar de plantearlo de una manera diferente.
Escribir los valores de la secuencia desde 1 hasta el 10 
Escribir 1; y luego Escribir los valores desde 2 hasta 10 
Escribir 2; y luego Escribir los valores desde 3 hasta 10 
...
Tema5- Diseño de programas recursivos
Programación I
153
Para obtener una solución ejecutable deberemos seguir unos pasos:
 Definición del problema:
escribir valores desde I hasta 10
Parametrización:
procedure Escribir_Hasta_10( I: in Integer)
-- pre: I expresa el número a partir del cual hay que escribir
-- post: Se han escrito los valores desde I hasta 10
 ¿Acción a repetir?
escribir I
 ¿Cuándo acaba el proceso?
I > 10
Tema5- Diseño de programas recursivos
154
Programación I
procedure Escribir_Hasta_10(I: in Integer) is
begin
if I > 10
if I <= 10
then no hacer nada
then Put(I);
else Put(I);
Escribir_Hasta_10 (I+1);
Escribir_Hasta_10(I+1);
end if;
end if;
end;
De esta manera:
Escribir_Hasta_10(1)put(1), Escribir_Hasta_10(2)put(2), Escribir_Hasta_10(3)  put(3), Escribir_Hasta_10(4) 
put/4), Escribir_Hasta_10(5)  put(5), Escribir_Hasta_10(6)  put(6), Escribir_Hasta_10(7)  put(7),
Escribir_Hasta_10(8)  put(8), Escribir_Hasta_10(9)  put(9), Escribir_Hasta_10(10)  put(10),
Escribir_Hasta_10(11)  ya no hace nada mas
Terminación cuando I > 10
¿Siempre termina?
Tema5- Diseño de programas recursivos
Programación I
155
Tema5- Diseño de programas recursivos
156
Programación I
Ejemplo2. Función recursiva para el cálculo del factorial.
Versión iterativa:
function Factorial(N: Natural) return Natural is
-- pos: devuelve N!
F: Integer;
begin
F:=1 ;
for I in 2..N loop
F:= F * I ;
end loop;
return F;
1
n=0 o n=1
end Factorial;
fac(n)=
n * fac(n-1)
e.o.c.
Versión iterativa:
 Definición del problema: factorial de N
Parametrización (igual que la versión iterativa)
function Factorial( N: in natural) return natural;
-- pos: devuelve N!
Tema5- Diseño de programas recursivos
Programación I
157
 ¿Cuándo acaba el proceso? O ¿cuándo se resolvería el problema directamente sin
recursión?
N <= 1
function Factorial(N: in Integer) return Integer is
F: Integer;
begin
if N <= 1 then
F := 1;
else
F := N * Factorial (N-1);
end if;
return F;
end Factorial;
Tema5- Diseño de programas recursivos
158
Programación I
Claves de la recursividad:
1.- La recursividad es otra alternativa para la solución de problemas (muchas veces existe una
alternativa iterativa)
2.- En ADA se pueden definir funciones y procedimientos recursivos
3.- En todo subprograma recursivo debe haber al menos un caso no recursivo
Casos no recursivos  El resultado se obtiene sin hacer llamadas recursivas (casos
triviales)
Subprograma sin casos triviales (implícito o explícito)  no acabaría jamás
4.- Los casos recursivos deben acercarse a los casos triviales, es decir, el subprograma debe ir
hacia su finalización.
5.- Cuando se hacen llamadas recursivas se crean diferentes instancias de los parámetros
formales y de las variables locales.
Tema5- Diseño de programas recursivos
Programación I
159
5.2. Etapas en el diseño de programas recursivos
Vamos a ver cada uno de estos pasos con un ejemplo. Ejemplo 3: “Dado un entero positivo
N, diseñar un algoritmo recursivo que escriba en pantalla el número con sus cifras en orden
inverso. Por ejemplo, el inverso de 3456 es 6543, con los mismos dígitos en orden invertido
(será útil el operador rem o mod para separar las cifras: 1234 rem 10 = 4).”
1. ESPECIFICACION/PARAMETRIZACIÓN
- Determinar los parámetros (fundamental para el diseño). Parámetros IN, IN OUT y OUT
- Especificar: Definir qué hace el algoritmo en función de esos parámetros (qué resuelve
cada subproblema)
- Establecer cuál será la llamada inicial en función de los parámetros definidos.
procedure Escribir_Inverso (N: in Natural)
-- Pre: N = d1d2...dn, donde d1, d2, ..., dn son dígitos de N
-- Salida: un número (salida estándar)
-- Pos: el número escrito es M= dndn-1...d1
primera llamada => Escribir_Inverso (Num)
Tema5- Diseño de programas recursivos
160
Programación I
2. ANALISIS DE LOS CASOS. Todos los casos posibles deben estar contemplados en los
casos triviales o casos recursivos o generales
TRIVIALES: Determinar para qué valores de los parámetros (bajo qué condiciones), existe
una solución directa y cuál es esa solución. Debe existir como mínimo 1 caso trivial, si
no el algoritmo no parará de generar subproblemas.
si N tiene un solo dígito, la solución es escribir N
GENERALES:
- Determinar para qué valores de los parámetros la solución se obtiene por
descomposición en subproblemas del mismo tipo.
- Establecer cómo se calculan esa solución general con las llamadas recursivas
correspondientes.
si N = 3456, entonces podemos escribir su última cifra (6),
y a continuación se escribe el resto de los dígitos (345)
en orden inverso: 6= N mod 10 y 345= N/10
si N tiene más de un dígito, la solución es
escribir (N rem 10) y Escribir_Inverso(N/10);
- Comprobar que las llamadas recursivas usan exactamente los parámetros definidos
- Comprobar que el tamaño del problema se reduce o tiende hacia los casos triviales.
Tema5- Diseño de programas recursivos
Programación I
161
Tema5- Diseño de programas recursivos
162
Programación I
3. DISEÑO DEL ALGORITMO ( y quizás las estructuras de datos)
 Agrupar lo diseñado en pasos previos, procurando simplificar el algoritmo cuando sea
posible.
 Eliminar redundancias y agrupar casos triviales cuando sea interesante.
 Procurar la eliminación de cálculos repetidos originada por el estudio separado de casos.
if (N / 10 = 0) then -- también podría ser N<10
Put (N);
else
Put (N rem 10);
Escribir_Inverso (N / 10); Put (N rem 10);
end if;
if (N/10 /= 0) then – N>9
Escribir_Inverso (N / 10);
end if;
Tema5- Diseño de programas recursivos
Programación I
163
4. PRUEBA DE TERMINACIÓN
Se debe comprobar que en las llamadas recursivas se “avanza” hacia un caso trivial, y que las
llamadas recursivas acabarán alguna vez.
N es un natural y tiene por ejemplo n dígitos N= d1d2...dn
En cada llamada al hacer N/10 pierde un dígito, y por tanto
llegará a ser un número de un sólo dígito.
5. IMPLEMENTACION
 Implementar las estructuras de datos, adecuadas a la representación de los mismos.
 Construir el subprograma (procedimiento o función) recursivo (en Ada en nuestro caso).
procedure Escribir_Inverso (N : in Integer)
-- Pre: N = d1d2...dn, donde d1, d2, ..., dn son dígitos de N
-- Salida: un número (salida estándar)
-- Pos: el número escrito es M= dndn-1...d1
begin
Put (N rem 10);
if (N / 10 /= 0) then
Escribir_Inverso (N / 10);
end if;
end Escribir_Inverso;
Tema5- Diseño de programas recursivos
164
Programación I
5.3. Ejemplos de Programas recursivos
Ejemplo4: Búsqueda lineal
type t_vector is array( integer range <>) of integer;
1. ESPECIFICACIÓN/PARAMETRIZACIÓN
Entrada: un elemento e de tipo integer y un vector t de elementos de tipo integer
Pre:
Salida: un booleano
Post: cierto sii el elemento e aparece en t
function esta(t: t_ vector; e: integer) return boolean;
-- pre:
-- post: devuelve cierto si el elemento e esta en t
Primera llamada => esta(t, e)
Tema5- Diseño de programas recursivos
Programación I
165
2. ANÁLISIS DE CASOS
 TRIVIALES:
El vector es vacío (índice superior < índice inferior),  falso.
El elemento e coincide con el primero del vector, cierto.
 GENERALES:
El elemento e NO COINCIDE con el central del vector,  esta (e, siguientes_vector).
3. DISEÑO DE ALGORITMOS
si es_vacío(t), falso
sinosi t(primero)=e, true
sino esta(siguientes(t), e)
4. PRUEBA DE TERMINACIÓN
Cuando vamos llamando a los siguientes de un vector llega un momento que nos
quedamos con un vector vacío, si es que antes no hemos encontrado ningún elemento
igual al buscado.
Tema5- Diseño de programas recursivos
166
Programación I
5. IMPLEMENTACIÓN
function esta(t: t_vector; e: integer) return boolean;
type t_tabla is array(integer range<>) of natural;
-- pre:
-- post: devuelve cierto si el elemento e esta en t.
begin
if t’last < t’first then return false;
elsif t(t’first)=e then return true;
else return esta(t(t’first +1.. t’last), e);
end if;
end esta;
7. Buscar en lista no ordenada
Para reflexionar y diseñar:
Dados un vector de enteros no ordenada y un valor entero, diseña un algoritmo recursivo
que si el valor pertenece al vector devuelva la posición en la que aparece y si no pertenece
al array, devuelva 0.
Tema5- Diseño de programas recursivos
Programación I
167
Ejemplo5. Búsqueda dicotómica, en un secuencia ordenada
type t_vector is array( integer range <>) of integer;
1. ESPECIFICACIÓN/PARAMETRIZACIÓN
Entrada: un elemento e de tipo integer y un vector t de elementos de tipo integer
Pre: los elementos de t están ordenados crecientemente
Salida: un booleano
Post: cierto sii el elemento e aparece en t
function esta(t: t_vector; e: integer) return boolean;
-- pre: t(i)t(i+1) para cualquier valor i:1..(ultimo de t-1)
-- post: devuelve cierto si el elemento e esta en t
Primera llamada => esta(t, e)
Tema5- Diseño de programas recursivos
168
Programación I
2. ANÁLISIS DE CASOS
 TRIVIALES:
El vector es vacío (índice superior < índice inferior),  falso.
El elemento e coincide con el central del vector, cierto.
 GENERALES:
El elemento e ES MENOR que el central de la vector,  esta (e, mitad_izq_tabla).
El elemento e ES MAYOR que el central de la vector,  esta(e, mitad_dch_tabla).
3. DISEÑO DE ALGORITMOS
si es_vacío(t), falso
sinosi t(mitad)=e, cierto
sinosi t(mitad)>e, esta(e, mitad_izq(t))
sino esta(e, mitad_dch(t))
4. PRUEBA DE TERMINACIÓN
Cuando vamos haciendo mitades de un vector llega un momento que nos quedamos con un
vector vacío. Por ejemplo; tenemos el vector t(3..3) y t(3) =4, e= 2, cuando tengo que
calcular la mitad izquierda lo haría:
mitad_izq(A(i..j)) = A(i..m-1), siendo m la posición de la mitad entre i y j, m= (i+j)/2
Por tanto, quedaría en el ejemplo t(3..3-1)= t(3..2) (índice superior < al inferior)
Tema5- Diseño de programas recursivos
Programación I
169
5. IMPLEMENTACIÓN
function esta(t: t_vector; e: integer) return boolean;
t_tabla is
array(integer
range<>)
natural;
-- pre: t(i)type
 t(i+1)
para
cualquier
valorofi:1..(longitud-1)
-- post: devuelve cierto si el elemento e esta en t.
mitad: integer;
begin
mitad:= (t’first + t’last)/2;
if t’last < t’first then return false;
elsif t(mitad)=e then return true;
elsif t(mitad)> e
then return esta(t(t’first..mitad –1), e);
else return esta(t(mitad +1.. t’last), e);
end if;
end esta;
Para reflexionar y diseñar:
Dada un array de enteros ordenados (de menor a mayor) y un valor entero, diseña un algoritmo recursivo que
devuelva la posición en la que aparece dicho elemento, si el valor pertenece al array, o la posición en que debería
colocarse si el elemento no pertenece al array.
Tema5- Diseño de programas recursivos
170
Programación I
Ejercicios
1. Escribir una secuencia en orden inverso. Dada una secuencia en la entrada estándar acabada en punto, diseña un
algoritmo recursivo que escriba los caracteres de la secuencia en orden inverso, sin escribir el punto.
2. Palíndromo. Tenemos palabras palíndromas “rapar”, “somos”, “allá”, “aléjela” o “reconocer”, un pueblo de nombre
palindromo: Lolol. Hay nombres propios que son palabra palindroma como Ana, Odo o Natán; y también apellidos: Laval,
Salas o Isasi…o frases como “ATALE DEMONIACO CAIN O ME DELATA”
A) Dadas las siguientes definiciones:
Max: constant Natural := 9;
subtype t_Indice is Integer range 1..Max;
type t_Tabla_de_Caracteres is array(t_Indice) of Character;
Diseña el procedimiento Comprobar_Palindromo utilizando un algoritmo recursivo
procedure Comprobar_Palindromo(T: in t_tabla_de_Caracteres;
Palindromo: out Boolean; Izquierda, Derecha: in t_indice);
-- Pre:
-- Post: Palindromo será True si y solo si para todo i,
-- Izquierda <= i <= Derecha, T(i) = T(Max-i+1)
-- también lo podríamos expresar, T(i)= T(Izquierda-i+derecha
Para verificar si un array es un palíndromo, la llamada inicial será de esta forma:
T: t_Tabla_de_Caracteres:= (r,e,c,o,n,o,c,e,r);
Es_Palindroma: boolean;
Comprobar_Palindromo (Tabla, Es_Palindroma, 1, Max);
Tema5- Diseño de programas recursivos
Programación I
171
B) Para facilitar el proceso de verificación con distintos tamaños de palabras vamos a resolver el problema con el tipo
de dato string. Recordar el proceso de recursión realizado en los problemas de búsqueda lineal y búsqueda
dicotómica.
funtion Comprobar_Palindromo(T: in string; Palindromo: out Boolean);
-- Pre:
-- Post: Palindromo será True si y solo si para todo i, primer índice de T
-- <= i <= último índice de T, T(i) = T(último-i+primer)
Para verificar si una palabra es palíndroma, las llamadas iniciales podrían ser:
Comprobar_Palidroma(“rapar” , Es_Palindroma );
Comprobar_Palidroma(“somos”, Es_Palindroma );
Comprobar_Palidroma(“allá”, Es_Palindroma );
Comprobar_Palidroma(“aléjela”, Es_Palindroma );
Comprobar_Palidroma(“reconocer”, Es_Palíndroma);
3. Ejercicios con vector de enteros:
type t_vector is array( integer range <>) of integer;
 Suma de los elementos de un vector. Dado un vector de enteros V, diseña un algoritmo recursivo que calcule la
suma de todos los componentes del vector.
 Número de elementos de una lista menores que otro. Dado un vector de enteros y un entero, diseña un
algoritmo recursivo que calcule cuántos elementos de la lista son menores que el entero dado.
 Escribir lista. Dado un vector de enteros, diseña un algoritmo recursivo que escriba en pantalla los elementos de
la lista en el mismo orden.
 Prefijo. Dados dos vectores de enteros decir si el segundo es prefijo del primero.
4. Ejercicios con vector de enteros de tamaño variable en ejecución:
type t_vector is array(integer range <>) of integer;
Max: contant integer:= 25;
type t_vector_variable is record
Tema5- Diseño de programas recursivos
172
Programación I
lista: t_vector(1..Max);
cuantos: integer range 0..Max;
end record;
 Insertar en lista ordenada. Dado un vector de enteros ordenada y un valor entero, diseña un algoritmo recursivo
que inserte el valor en el lugar apropiado.
 Borrar la primera aparición. Dado un vector de enteros y un valor entero, diseña un algoritmo recursivo que
borre ese elemento de la lista.
 Borrar en lista ordenada. Dada una lista de enteros ordenada y un valor entero, diseñar un algoritmo recursivo
que borre ese elemento de la lista.
5. Implementa el subprograma Exponencial en Ada de forma recursiva. La exponencial son multiplicaciones sucesivas:
NM = N*N*N*…*N (multiplicado M veces).
function Exponencial(N,M:Natural) return Natural is
--pre:
--post: El resultado es NM
procedure Exponencial(N,M:Natural; Resultado: out Natural) is
--pre:
--post: Resultado = NM
6. Diseña e Implementa un procedimiento en Ada con la siguiente especifiación:
procedure Dar_la_vuelta ( S:String; Resultado: out String) is
--post: Resultado es la palabra S dada la vuelta
7. Diseña e implementa un algoritmo recursivo que dados dos naturales N y M que verifican que N  M, escriba en la
salida externa (pantalla) los divisores de M mayores o iguales a N.
8. Diseña e implementa un algoritmo recursivo que dada una matriz cuadrada de tamaño NxN, decida si la matriz es
simétrica o no con respecto a la diagonal ((1,1), (2,2), ..., (n,n))
Tema5- Diseño de programas recursivos
Programación I
173
9. Se pide diseñar un algoritmo recursivo que, dado un natural N > 0, escriba en la salida externa todas las
permutaciones con repetición de N elementos tomados de N en N. Ejemplo: si N = 2, aparecerán en pantalla los pares
(1,1), (1,2), (2,1), (2,2). Se pide su implementación en Ada de forma recursiva.
Tema5- Diseño de programas recursivos
Descargar