to get the file - OCW - Universidad de Murcia

Anuncio
Tecnología de la Programación
Título de Grado en Ingeniería Informática
Curso 2009/10
Fernando Jiménez Barrionuevo
Gracia Sánchez Carpena
Mari Carmen Garrido Carrera
Departamento de Ingeniería de la Información de las Comunicaciones
Universidad de Murcia
Temario
Tema 1. Recursividad
1.1
1.2
1.3
1.4
1.5
Definición de recursividad
Recursividad en C
Esquemas recursivos
Tiempo de ejecución de los algoritmos recursivos
Recursión vs iteración
Tecnología de la Programación
Tema 1. Recursividad
2
1.1 Definición de Recursividad
Se dice que un objeto es recursivo si forma parte de sí mismo o se
define en función de sí mismo.
Técnica particularmente potente en las definiciones matemáticas.
La potencia de la recursión reside evidentemente en la posibilidad de
definir un número infinito de objetos mediante un enunciado finito.
De igual forma, un número infinito de operaciones de cálculo puede
describirse mediante un programa recursivo finito.
En general, un programa recursivo P puede expresarse como una
composición ℘ de instrucciones básicas Si (que no contienen a P)
y el propio P.
P≡℘[Si,P]
Tecnología de la Programación
Tema 1. Recursividad
3
1.1 Definición de Recursividad
Ejemplos:
Números naturales:
1 es un número natural
El siguiente de un número natural es un número natural
Función factorial, n! (para números enteros no negativos):
0! = 1
Si n>0 entonces n! = n · (n–1)!
3! =
=
=
=
=
=
=
=
3
3
3
3
3
3
3
6
·
·
·
·
·
·
·
(3-1)!
2!
2 · (2-1)!
2 · 1!
2 · 1 · (1-1)!
2 · 1 · 0!
2 · 1 · 1
Tecnología de la Programación
Tema 1. Recursividad
4
1.1 Definición de Recursividad
Un algoritmo es recursivo si se invoca a sí mismo al menos una
vez.
Recursividad directa:
El algoritmo contiene una llamada explícita a sí mismo, es decir, A
invoca a A.
Recursividad indirecta:
El algoritmo se invoca a sí mismo de forma indirecta, es decir, A
invoca a B y B invoca a A.
Recursividad de cola o extremo final:
La llamada recursiva es la última instrucción que ejecuta el
algoritmo.
Tecnología de la Programación
Tema 1. Recursividad
5
1.1 Definición de Recursividad
Un algoritmo recursivo está bien construido si:
1. Contiene, al menos, un caso base, en el cual el algoritmo
no se llama a sí mismo.
2. Los valores de los parámetros del algoritmo en las
sucesivas llamadas recursivas están más cerca del caso
base que el valor con el que se llamó inicialmente al
algoritmo, garantizando que el caso base se alcanza.
Tecnología de la Programación
Tema 1. Recursividad
6
1.2 Recursividad en C
Multiplicación n · m (m ≥ 0)
Caso Base:
(m = 0) n · 0 = 0
Caso General: (m > 0) n · m = n + n · (m - 1)
// m>=0
int multiplica(int n, int m)
{
if (m==0) return 0;
return n+multiplica(n,m-1);
}
// m>=0 (version comprimida)
int multiplica(int n, int m)
{
return(m==0?0:n+multiplica(n,m-1));
}
Tecnología de la Programación
Tema 1. Recursividad
7
1.2 Recursividad en C
División entera n / m (n ≥ 0 y m>
>0)
Caso Base:
(n < m) n / m = 0
Caso General: (n ≥ m) n / m = 1 + ( n – m ) / m
// n>=0, m>0
int divide(int n, int m)
{
if (n<m) return 0;
return 1+divide(n-m,m);
}
// n>=0 y m>0 (version comprimida)
int divide(int n, int m)
{
return (n<m?0:1+divide(n-m,m));
}
Tecnología de la Programación
Tema 1. Recursividad
8
1.2 Recursividad en C
Módulo n % m (n ≥ 0 y m > 0)
Caso Base:
(n < m) n % m = n
Caso General: (n ≥ m) n % m = ( n – m ) % m
// n>=0, m>0
int modulo(int n, int m)
{
if (n<m) return n;
return modulo(n-m,m);
}
// n>=0 y m>0 (version comprimida)
int modulo(int n, int m)
{
return (n<m?n:modulo(n-m,m));
}
Tecnología de la Programación
Tema 1. Recursividad
9
1.2 Recursividad en C
Potencia nm (m ≥ 0)
Caso Base:
(m = 0) n 0 = 1
Caso General: (m > 0) n m = n · n m - 1
// m>=0
int potencia(int n, int m)
{
if (m==0) return 1;
return n*potencia(n,m-1);
}
// m>=0 (version comprimida)
int potencia(int n, int m)
{
return(m==0?1:n*potencia(n,m-1));
}
Tecnología de la Programación
Tema 1. Recursividad
10
1.2 Recursividad en C
Factorial n! (n ≥ 0)
Caso Base:
(n = 0) 0! = 1
Caso General: (n > 0) n! = n · ( n – 1 )!
// n>=0
int factorial(int n)
{
if (n==0) return 1;
return n*factorial(n-1);
}
// n>=0 (version comprimida)
int factorial(int n)
{
return(n==0?1:n*factorial(n-1));
}
Tecnología de la Programación
Tema 1. Recursividad
11
1.2 Recursividad en C
Fibonacci fib(n) (n ≥ 0)
Caso Base:
(n ≤ 1) fib(n) = n
Caso General: (n > 1) fib(n) = fib(n-2) + fib(n-1)
// n>=0
int fibonacci(int n)
{
if (n<=1) return n;
return fibonacci(n-2)+fibonacci(n-1);
}
// n>=0 (version comprimida)
int fibonacci(int n)
{
return(((n<=1))?n:fibonacci(n-2)+fibonacci(n-1));
}
Tecnología de la Programación
Tema 1. Recursividad
12
1.2 Recursividad en C
Hanoi h(n,a,b,c) (n ≥ 1)
Caso Base:
(n = 1) h(1,a,b,c): a → c
Caso General: (n > 1) h(n,a,b,c): { h(n-1,a,c,b), a → c, h(n-1,b,a,c) }
// n>=1
void hanoi(int n, char a, char b, char c)
{
if (n>1) hanoi(n-1,a,c,b);
printf(“Mover de %d a %d\n”,a,c);
if (n>1) hanoi(n-1,b,a,c);
}
Tecnología de la Programación
Tema 1. Recursividad
13
1.3 Esquemas recursivos
Divide y Vencerás
función DV(x) devuelve solución
si x es suficientemente pequeño o sencillo entonces (Caso Base):
devolver ad hoc (x)
en otro caso (Caso General):
descomponer x en casos más pequeños x1,x2,...,xn
para i=1 hasta n hacer yi = DV(xi)
combinar los yi para obtener una solución y de x
devolver y
Tecnología de la Programación
Tema 1. Recursividad
14
1.3 Esquemas recursivos
Divide y Vencerás: Búsqueda Binaria
Busca un elemento e en un vector v ordenado.
Devuelve el índice de una ocurrencia del elemento e en del vector v.
Si el vector v no contiene ninguna ocurrencia del elemento e, devuelve -1.
// v está ordenado
int buscabin(int v[], int ini, int fin, int e)
{
if (ini>fin) return -1;
int med = (ini+fin)/2;
if (v[med]==e) return med;
if (v[med]>e) return buscabin(v,ini,med-1,e);
if (v[med]<e) return buscabin(v,med+1,fin,e);
}
// v está ordenado, tam es el numero de elementos de v
int buscabin(int v[], int tam, int e)
{
return buscabin(v,0,tam,e);
}
Tecnología de la Programación
Tema 1. Recursividad
15
1.3 Esquemas recursivos
Divide y Vencerás: Ordenación por Mezcla
Ordena un vector v desde el índice ini hasta el índice fin.
void mergesort(int v[], int ini, int fin)
{
if ((fin-ini)>0)
{
int med = (ini+fin)/2;
mergesort(v,ini,med);
mergesort(v,med+1,fin);
mezcla(v,ini,fin,med);
}
}
// tam es el numero de elementos de v
void mergesort(int v[], int tam)
{
mergesort(v,0,tam);
}
Tecnología de la Programación
Tema 1. Recursividad
16
1.3 Esquemas recursivos
Vuelta Atrás: Una solución
Devuelve cierto si encuentra una solución a partir de un paso, y falso en caso
contrario. Si encuentra una solución la devuelve en el parámetro solucion.
funcion VA(entrada: paso, salida: solución) devuelve booleano
si solución completa entonces devolver cierto fin si
para cada posibilidad de solución
si posibilidad aceptable en solución entonces
anotar posibilidad en solución
si VA(siguiente paso, solución) entonces devolver cierto fin si
cancelar posibilidad en solución
fin si
fin para
devolver falso
Tecnología de la Programación
Tema 1. Recursividad
17
1.3 Esquemas recursivos
Vuelta Atrás: Una solución
Solución compuesta por n pasos y m posibilidades para cada paso.
El parámetro solucion debe pasarse como apuntador porque es de salida.
bool VA(int i, solucion * s)
{
if (i==n) return true;
for(int j=0;j<m;j++)
if (aceptable(s,i,j))
{
anotar(s,i,j);
if (VA(i+1,s)) return true;
cancelar(s,i,j);
}
return false;
}
LLamada inicial al método VA:
solucion * s = new solucion();
inicia(s);
if (VA(0,s)) imprime(s);
Tecnología de la Programación
Tema 1. Recursividad
18
1.3 Esquemas recursivos
Vuelta Atrás: Todas las soluciones
Obtiene todas las soluciones posibles a partir de un paso.
funcion VA(entrada: paso, salida: solución)
si solución completa entonces anotar solución
en otro caso
para cada posibilidad de solución
si posibilidad aceptable en solución entonces
anotar posibilidad en solución
VA(siguiente paso, solución)
cancelar posibilidad en solución
fin si
fin para
fin si
Tecnología de la Programación
Tema 1. Recursividad
19
1.3 Esquemas recursivos
Vuelta Atrás: Todas las soluciones
Solución compuesta por n pasos y m posibilidades para cada paso.
void VA(int i, solucion * s)
{
if (i==n) imprime(s);
else
for(int j=0;j<m;j++)
if (aceptable(s,i,j))
{
anotar(s,i,j);
VA(i+1,s);
cancelar(s,i,j);
}
}
LLamada inicial al método VA:
solucion s = new solucion();
inicia(s);
VA(0,s));
Tecnología de la Programación
Tema 1. Recursividad
20
1.3 Esquemas recursivos
Vuelta Atrás: Ocho Reinas
n = 8 (8 pasos, un paso por cada reina)
m = 8 (8 posibilidades para cada reina)
x[i]: indica la posición en columna de la reina en la fila i-ésima
a[i]: true si la columna i-ésima está libre
b[i]: true si la diagonal derecha i-ésima está libre
c[i]: true si la diagonal izquierda i-ésima está libre
struct solucion
{
int x[8];
bool a[8];
bool b[15];
bool c[15];
};
Tecnología de la Programación
Tema 1. Recursividad
21
1.3 Esquemas recursivos
Vuelta Atrás: Ocho Reinas
void inicia(solucion * s)
{
for(int j=0;j<8;j++) s->a[j] = true;
for(int j=0;j<15;j++) { s->b[j] = true; s->c[j] = true; }
}
void imprime(solucion * s)
{
for(int j=0;j<8;j++) printf("%d ",s->x[j]); printf("\n");
}
bool aceptable(solucion * s,int i,int j)
{
return s->a[j]&&s->b[i+j]&&s->c[i-j+7];
}
void anotar(solucion * s, int i, int j)
{
s->x[i]=j; s->a[j]=false; s->b[i+j]=false; s->c[i-j+7]=false;
}
void cancelar(solucion * s, int i, int j)
{
s->a[j]=true; s->b[i+j]=true; s->c[i-j+7]=true;
}
Tecnología de la Programación
Tema 1. Recursividad
22
1.4 Tiempo de ejecución de los algoritmos recursivos
Expansión de recurrencias
Utiliza la recurrencia misma para sustituir m<n por
cualquier T(m) en la derecha, hasta que todos los términos
T(m) para m>1 se hayan reemplazado por fórmulas que
impliquen sólo T(1).
Como T(1) siempre es constante, se tiene una fórmula
para T(n) en función de n y de algunas constantes.
A esta fórmula se le denomina “forma cerrada” para T(n).
Tecnología de la Programación
Tema 1. Recursividad
23
1.4 Tiempo de ejecución de los algoritmos recursivos
Ejemplo: Factorial
Ecuación de recurrencia:
T(0) = c1
T(n) = T(n-1) + c2
si n>0
Expansión de recurrencias:
T(n) = T(n-1)+c2 = [T(n-2)+c2]+c2 = T(n-2)+2c2 = [T(n-3)+c2]+2c2 = T(n-3)+3c2
Forma cerrada para T(n) (por inducción en i):
T(n) = T(n-i) + i c2
La expansión terminará cuando se alcance T(0) en lado derecho de la forma cerrada,
es decir, cuando n-i=0, por tanto i=n. Sustituyendo en la forma cerrada i por n se
obtiene:
T(n) = T(0) + n c2 = c1 + n c2
Esto demuestra que T(n) es O(n).
Tecnología de la Programación
Tema 1. Recursividad
24
1.4 Tiempo de ejecución de los algoritmos recursivos
Ejemplo: Búsqueda Binaria
Ecuación de recurrencia:
T(1) = c1
T(n) = T(n/2) + c2
si n>1
Expansión de recurrencias:
T(n) = T(n/2)+c2 = [T(n/4)+c2]+c2 = T(n/4)+2c2 = [T(n/8)+c2]+2c2 = T(n/8)+3c2
Forma cerrada para T(n) (por inducción en i):
T(n) = T(n/2i) + ic2
La expansión terminará cuando se alcance T(1) en lado derecho de la forma cerrada, es
decir, cuando n/2i=1, por tanto i=log n. Sustituyendo en la forma cerrada i por n se
obtiene:
T(n) = T(1) + log n c2 = c1 + log n c2
Esto demuestra que T(n) es O(log n).
Tecnología de la Programación
Tema 1. Recursividad
25
1.4 Tiempo de ejecución de los algoritmos recursivos
Ejemplo: Mergesort
Ecuación de recurrencia:
T(1) = c1
T(n) = 2 T(n/2) + n c2
si n>1
Expansión de recurrencias:
T(n) = 2T(n/2)+nc2 = 2[2T(n/4)+(n/2)c2]+nc2 = 4T(n/4)+2nc2 = 8T(n/8)+3nc2
Forma cerrada para T(n) (por inducción en i):
T(n) = 2i T(n/2i) + i n c2
La expansión terminará cuando se alcance T(1) en lado derecho de la forma cerrada, es
decir, cuando n/2i=1, por tanto i=log n. Sustituyendo en la forma cerrada i por n se
obtiene:
T(n) = nT(1) + (log n) n c2 = nc1 + n log n c2
Esto demuestra que T(n) es O(n log n).
Tecnología de la Programación
Tema 1. Recursividad
26
1.5 Recursión vs Iteración
La recursión implica múltiples llamadas y el uso de la pila interna para
almacenar, en cada llamada recursiva, los parámetros de llamada,
variables locales y dirección de retorno, lo que hace que en general
sea más ineficiente que el diseño iterativo.
Siempre es posible sustituir un algoritmo recursivo por un algoritmo
iterativo que utilice una pila.
Casos en los que no se debe utilizar la recursión:
1.
2.
3.
Recursividad de cola
Árbol de recursión lineal (ejemplo, factorial)
Árbol de recursión ramificado, pero con nodos repetidos (ejemplo
Fibonacci)
Conclusión: la recursión se deberá utilizar cuando, además de facilitar el
diseño del algoritmo, genere un árbol de recursión ramificado y con
nodos no repetidos.
Tecnología de la Programación
Tema 1. Recursividad
27
1.5 Recursión vs Iteración
Multiplicación n · m (m ≥ 0)
Caso Base:
(m = 0) n · 0 = 0
Caso General: (m > 0) n · m = n + n · (m - 1)
// m>=0 (versión recursiva)
int multiplica(int n, int m)
{
if (m==0) return 0;
return n+multiplica(n,m-1);
}
// m>=0 (versión iterativa)
int multiplica(int n, int m)
{
int res = 0;
for(;m>0;m--) res+=n;
return res;
}
Tecnología de la Programación
Tema 1. Recursividad
28
1.5 Recursión vs Iteración
División entera n / m (n ≥ 0 y m > 0)
Caso Base:
(n < m) n / m = 0
Caso General: (n ≥ m) n / m = 1 + ( n – m ) / m
// n>=0, m>0 (versión recursiva)
int divide(int n, int m)
{
if (n<m) return 0;
return 1+divide(n-m,m);
}
// n>=0, m>0 (versión iterativa)
int divide(int n, int m)
{
int res = 0;
for(;n>=m;n-=m) res++;
return res;
}
Tecnología de la Programación
Tema 1. Recursividad
29
1.5 Recursión vs Iteración
Módulo n % m (n ≥ 0 y m > 0)
Caso Base:
(n < m) n % m = n
Caso General: (n ≥ m) n % m = ( n – m ) % m
// n>=0, m>0 (versión recursiva)
int modulo(int n, int m)
{
if (n<m) return n;
return modulo(n-m,m);
}
// n>=0, m>0 (versión iterativa)
int modulo(int n, int m)
{
for(;n>=m;n-=m);
return n;
}
Tecnología de la Programación
Tema 1. Recursividad
30
1.5 Recursión vs Iteración
Potencia nm (m ≥ 0)
Caso Base:
(m = 0) n 0 = 1
Caso General: (m > 0) n m = n · n m - 1
// m>=0 (versión recursiva)
int potencia(int n, int m)
{
if (m==0) return 1;
return n*potencia(n,m-1);
}
// m>=0 (versión iterativa)
int potencia(int n, int m)
{
int res = 1;
for(;m>0;m--) res*=n;
return res;
}
Tecnología de la Programación
Tema 1. Recursividad
31
1.5 Recursión vs Iteración
Factorial n! (n ≥ 0)
Caso Base:
(n = 0) 0! = 1
Caso General: (n > 0) n! = n · ( n – 1 )!
// n>=0 (versión recursiva)
int factorial(int n)
{
if (n==0) return 1;
return n*factorial(n-1);
}
// n>=0 (version iterativa)
int factorial(int n)
{
int res = 1;
for(;n>0;n--) res*=n;
return res;
}
Tecnología de la Programación
Tema 1. Recursividad
32
1.5 Recursión vs Iteración
Fibonacci fib(n) (n ≥ 0)
Caso Base:
(n ≤ 1) fib(n) = n
Caso General: (n > 1) fib(n) = fib(n-2) + fib(n-1)
// n>=0 (versión recursiva)
int fibonacci(int n)
{
if ((n<=1)) return n;
return fibonacci(n-2)+fibonacci(n-1);
}
// n>=0 (version iterativa)
int fibonacci(int n)
{
if (n<=1) return n;
int * aux = (int *)malloc((n+1)*sizeof(int));
aux[0] = 0;
aux[1] = 1;
for(int i=2; i<=n; i++) aux[i] = aux[i-2] + aux[i-1];
int res = aux[n];
free(aux);
return res;
}
Tecnología de la Programación
Tema 1. Recursividad
33
1.5 Recursión vs Iteración
Búsqueda Binaria
// v está ordenado (versión recursiva)
int buscabin(int v[], int ini, int fin, int e)
{
if (ini>fin) return -1;
int med = (ini+fin)/2;
if (v[med]==e) return med;
if (v[med]>e) return buscabin(v,ini,med-1,e);
if (v[med]<e) return buscabin(v,med+1,fin,e);
}
// v está ordenado (versión iterativa)
int buscabin(int v[], int ini, int fin, int e)
{
while(ini<=fin)
{
int med = (ini+fin)/2;
if (v[med]==e) return med;
if (v[med]>e) fin = med-1; else ini = med+1;
}
return -1;
}
Tecnología de la Programación
Tema 1. Recursividad
34
Ejercicios Propuestos
Máximo Común Divisor mcd(n,m) (n ≥ 0, m ≥ 0)
Caso Base:
(m == 0)
Caso General: (m > 0)
mcd(n,m) = n
mcd(n,m) = mcd(m,n%m)
Monedas:
Calcular el número mínimo de monedas necesarias para una
determinada cantidad.
Suponemos que tenemos monedas de 1, 5, 10, 20 y 50 céntimos y
que la cantidad es menor de 1 euro.
Tecnología de la Programación
Tema 1. Recursividad
35
Descargar