Recursividad

Anuncio
Algorítmica y Lenguajes de Programación
Recursividad
Recursividad.
!
!
!
!
!
Introducción
La recursividad es una característica de los lenguajes
de programación que permite que un subprograma
se invoque a sí mismo.
La recursividad es útil para resolver problemas
definibles en sus propios términos.
La recursividad es, en cierta medida, análoga al
principio de inducción.
No existen problemas intrínsecamente recursivos o
iterativos; cualquier proceso iterativo puede
expresarse de forma recursiva y viceversa.
La recursividad aunque da lugar a algoritmos más
simples y compactos resulta más lenta y consume
más recursos al ejecutarse sobre el ordenador.
2
1
Recursividad.
!
!
Para desarrollar algoritmos recursivos hay que
partir del supuesto de que ya existe un
algoritmo que resuelve una versión más sencilla
del problema.
A partir de esta suposición debe hacerse lo
siguiente:
1.
2.
3.
Identificar subproblemas atómicos de resolución
inmediata (casos base).
Descomponer el problema en subproblemas resolubles
mediante el algoritmo pre-existente; la solución de estos
subproblemas debe aproximarnos a los casos base.
Probar de manera informal que tanto los casos base
como los generales pueden solucionarse con el
algoritmo desarrollado.
3
Recursividad.
!
Algoritmos recursivos (i)
Algoritmos recursivos (ii)
Factorial de un número, n!
!
!
!
!
Algoritmo pre-existente: (n-1)!
Caso base: 0! = 1
Subproblemas resolubles mediante el
algoritmo pre-existente y que aproximen al
caso base: n! = n · (n-1)!
Prueba informal: Conociendo las dos
expresiones anteriores es posible calcular
0!, 1!, 4!, etc.
4
2
Recursividad.
!
Algoritmos recursivos (iii)
Potencia de un número, an
!
!
!
!
Algoritmo pre-existente: an-1
Caso base: a0 = 1
Subproblemas resolubles mediante el
algoritmo pre-existente y que aproximen al
caso base: an = a · an-1
Prueba informal: Conociendo las dos
expresiones anteriores es posible calcular
20, 43, 34, etc.
5
Recursividad.
!
!
!
Sintaxis notación algorítmica (i)
La sintaxis para el desarrollo de
funciones y subrutinas en la notación
algorítmica es totalmente transparente.
Basta con hacer la llamada
correspondiente dentro del cuerpo del
subprograma.
Tan sólo hay que tener la precaución de
no iniciar una llamada recursiva para los
casos base.
6
3
Recursividad.
!
Sintaxis notación algorítmica (ii)
Ejemplos:
entero funcion factorial (n ∈ entero)
inicio
si n=0 entonces
factorial " 1
si no
factorial " n · factorial(n-1)
fin si
fin
!
entero funcion potencia (a,n ∈ entero)
inicio
si n=0 entonces
potencia " 1
si no
potencia " a · potencia(a, n-1)
fin si
fin
Estas funciones no tienen validación de datos de
entrada.
7
Recursividad.
!
!
Sintaxis FORTRAN 90 (i)
FORTRAN 77 no soporta recursividad.
FORTRAN 90 lo hace pero de una forma
bastante artificiosa:
!
!
Hay que utilizar la palabra reservada RECURSIVE
para indicar que el subprograma es recursivo.
Las funciones precisan, además, utilizar una
variable de retorno (indicada mediante la palabra
reservada RESULT) puesto que FORTRAN 90 no
admite la aparición del nombre de una función
simultáneamente en la parte derecha e izquierda
de una asignación.
8
4
Recursividad.
!
Sintaxis FORTRAN 90 (ii)
Ejemplos:
integer recursive function factorial(n) result(f)
integer n
if (n==0) then
f = 1
else
f = n * factorial(n-1)
end if
end function
integer recursive function potencia(a,n) result (p)
integer a,n
if (n==0) then
p = 1
else
p = a * potencia(a,n-1)
end if
end function
recursive subroutine asteriscos (n)
Funciones
integer n
if (n==1) then
print *, '*'
else
Subrutina
print *, '*'
call asteriscos(n-1)
end if
end subroutine
Recursividad.
!
!
!
!
!
Invocación recursiva (i)
Al ejecutarse un programa existe una zona de
memoria, pila, donde se almacena información por
cada subprograma activo.
Esta información consiste en:
!
!
9
Los argumentos de la función o subrutina.
Las variables locales del subprograma.
La “dirección” de retorno, es decir el punto del programa que
debe pasar a ejecutarse cuando el subprograma retorne.
Cuando un subprograma es invocado toda esa
información es apilada.
Al finalizar la ejecución del subprograma es retirada y
en el caso de una función sustituida por el valor de
retorno.
10
5
Recursividad.
Invocación recursiva (ii)
Ejemplo (i)
01 program prueba
02
implicit none
03
04
integer valor
05
valor = factorial (3)
06
print *, valor
07
08
09
contains
10
11
integer recursive function factorial (n) result (f)
12
implicit none
13
integer n
14
15
if (n==0) then
16
f = 1
17
else
18
f = n*factorial(n-1)
19
end if
20
end function
21 end
Recursividad.
11
Invocación recursiva (iii)
Ejemplo (ii)
Argumentos:
Variables locales: valor (¿?)
Dirección retorno: Sistema
n (3)
Argumentos:
Variables locales: f (¿?)
Dirección retorno: programa (06)
Argumentos:
Variables locales: valor (¿?)
Dirección retorno: Sistema
Al comenzar a ejecutarse el programa la única variable del mismo, valor, carece de un
valor determinado.
En la línea 05 se invoca la función factorial, para lo cual se apilan sus argumentos, 3,
y la dirección de retorno. Al ser una función al retornar debe desapilarse esta información
y colocarse en la pila el valor de retorno, correspondiente a la línea 06 del programa
principal.
n (2)
Argumentos:
Variables locales: f (¿?)
Dirección retorno: factorial (19)
n (3)
Argumentos:
Variables locales: f (¿?)
Dirección retorno: programa (06)
Argumentos:
Variables locales: valor (¿?)
Dirección retorno: Sistema
La función factorial invocada debe, a su vez, invocarse a sí misma con lo cual debe
apilar los argumentos de esa nueva llamada, 2, y la dirección de retorno que se
corresponderá con la línea 19 asociada a la primera llamada de la función.
12
6
Recursividad.
n (1)
Argumentos:
Variables locales: f (¿?)
Dirección retorno: factorial (19)
Invocación recursiva (iv)
Ejemplo (iii)
n (2)
Argumentos:
Variables locales: f (¿?)
Dirección retorno: factorial (19)
n (3)
Argumentos:
Variables locales: f (¿?)
Dirección retorno: programa (06)
Argumentos:
Variables locales: valor (¿?)
Dirección retorno: Sistema
La segunda función factorial invocada debe invocar también otra copia de sí misma,
apilando los argumentos, 1, y la nueva dirección de retorno.
n (0)
Argumentos:
Variables locales: f (¿?)
Dirección retorno: factorial (19)
n (1)
Argumentos:
Variables locales: f (¿?)
Dirección retorno: factorial (19)
n (2)
Argumentos:
Variables locales: f (¿?)
Dirección retorno: factorial (19)
n (3)
Argumentos:
Variables locales: f (¿?)
Dirección retorno: programa (06)
Argumentos:
Variables locales: valor (¿?)
Dirección retorno: Sistema
La función factorial es invocada, otra vez, y se apilan los datos necesarios, otra vez.
13
Recursividad.
Invocación recursiva (v)
Ejemplo (iv)
1
n (1)
Argumentos:
Variables locales: f (¿?)
Dirección retorno: factorial (19)
n (2)
Argumentos:
Variables locales: f (¿?)
Dirección retorno: factorial (19)
Argumentos:
n (3)
Variables locales: f (¿?)
Dirección retorno: programa (06)
Como la última invocación de factorial recibe como argumento el 0, caso base,
detiene la serie de invocaciones recursivas y retorna el valor 1 desapilando los datos
sobre estado y apilando el valor de retorno.
Argumentos:
Variables locales: valor (¿?)
Dirección retorno: Sistema
Este valor de retorno es recibido por la penúltima invocación de factorial, pudiendo
finalizar y retornar, es decir, desapilar su información de estado y apilando su valor de
retorno, 1.
1
Argumentos:
n (2)
Variables locales: f (¿?)
Dirección retorno: factorial (19)
n (3)
Argumentos:
Variables locales: f (¿?)
Dirección retorno: programa (06)
Argumentos:
Variables locales: valor (¿?)
Dirección retorno: Sistema
Dicho valor de retorno es recibido por la segunda llamada a factorial que puede
también finalizar, desapilar su información de estado y apilar su valor de retorno, 2.
14
7
Recursividad.
Invocación recursiva (vi)
Ejemplo (v)
2
n (3)
Argumentos:
Variables locales: f (¿?)
Dirección retorno: programa (06)
Argumentos:
Variables locales: valor (¿?)
Dirección retorno: Sistema
Este valor de retorno es extraído de la pila por la primera invocación de factorial que
finaliza, desapila su información y coloca en la pila el valor de retorno final de la serie de
llamadas recursivas, 6.
Argumentos:
Variables locales: valor (¿?)
Dirección retorno: Sistema
Este valor de retorno es desapilado por el programa principal y asignado a la variable
valor.
6
Argumentos:
Variables locales: valor (6)
Dirección retorno: Sistema
!
!
El programa ha finalizado y puede retornar al sistema operativo.
Como se puede ver, una serie de llamadas recursivas supone un gran número
de operaciones con la pila de ejecución lo cual resulta más lento que una serie
de operaciones iterativas y, además, requiere un espacio considerable.
Por eso se dice que la recursividad es más lenta y consume más
recursos que una solución iterativa.
15
Recursividad.
!
!
!
!
!
Un subprograma es recursivo cuando se invoca a sí mismo.
La recursividad es análoga al principio de inducción.
Todo proceso recursivo puede convertirse en iterativo y viceversa.
Los algoritmos recursivos son más simples y compactos que sus
correspondientes iterativos, sin embargo, su ejecución en un ordenador es
más lenta y requiere más recursos.
Para diseñar un algoritmo recursivo hay que tener en cuenta lo siguiente:
!
!
!
!
!
!
Resumen
Suponer que ya existe un algoritmo para resolver una versión reducida del
problema.
Identificar casos base.
Descomponer el problema en subproblemas que se puedan resolver con el
algoritmo pre-existente y cuya solución vaya aproximándose a los casos base.
Probar, de manera informal, que los casos base y los generales encuentran
solución con el algoritmo diseñado.
La sintaxis de funciones y subrutinas recursivas en la notación algorítmica
es inmediata.
La sintaxis en FORTRAN 90 resulta un tanto artificiosa.
16
8
Descargar