Professional Documents
Culture Documents
Curso 2011/2012
Recursividad
Los trminos
recurrencia, recursin o recursividad hacen referencia a
una tcnica de definicin de conceptos (o de diseo de
procesos) en la que el concepto definido (o el proceso
diseado) es usado en la propia definicin
(o diseo).
Un ejemplo paradigmtico sera el del
tringulo de Sierpinski en el que cada
tringulo est compuesto de otro ms
pequeos, compuestos s su vez de la
misma estructura recursiva (de hecho en este caso se trata
de una estructura fractal)
Otro caso de estructura recursiva son
las denominadas Matryoshkas (o
muecas rusas): donde cada mueca
esconde en su interior otra mueca,
que esconde en su interior otra
mueca que , hasta que se llega a
una mueca que ya no escode nada.
En nuestro caso nos preocuparemos de los mtodos
(funciones o acciones) recursivos: aqullos en los que,
dentro de las instrucciones que los forman, contienen una
llamada a s mismos.
Como siempre, la parte ms compleja no ser a nivel de
programacin, sino a nivel de diseo: dado un problema,
ser capaz de encontrar una solucin recursiva del mismo.
Por tanto, deberemos ser capaces de pensar
recursivamente.
Algunos de los problemas que veremos ya los sabis
resolver iterativamente y es bueno comparar las soluciones
recursivas que veremos con las iterativas que podis
realizar por vuestra cuenta.
Programacin 2
Curso 2011/2012
1. Llamadas a funciones
Antes de empezar con las llamadas recursivas,
recordaremos brevemente cmo funcionan las llamadas
entre funciones y cmo stas modifican el flujo de
ejecucin.
Consideremos el siguiente ejemplo, que ya vimos en el
tema anterior:
1 /*
1 * File: SquareRoot.java
2 * --------------------3 * This program calculates the square root of a
4 * given positive integer
5 */
6
7 import acm.program.ConsoleProgram;
8
9 public class SquareRoot extends ConsoleProgram {
10
11 public int squareRoot(int n) {
12
int lower = 0;
13
while ((lower + 1) * (lower + 1) <= n) {
14
lower = lower + 1;
15
}
16
return lower;
17 }
18
19 public void run() {
20
int n = readInt("Enter a natural number: ");
21
int root = squareRoot(n);
22
println("The root is " + root);
23 }
24 }
Programacin 2
Curso 2011/2012
readInt
21
squareRoot
22
println
23
n: 15
lower: 0 1 2 3
Programacin 2
Curso 2011/2012
Programacin 2
Curso 2011/2012
Programacin 2
Curso 2011/2012
1 /* CheckPalindrome.java
2 * -------------------3 * Checks whether the entered text is palindrome.
4 */
5
6 import acm.program.ConsoleProgram;
7
8 public class CheckPalindrome extends ConsoleProgram {
9
10 public String removeSpaces(String text) {
11
// Ejercicio
12 }
13
14 public boolean isPalindrome(String text) {
15
// Se detallar ms adelante
16 }
17
18 public void run() {
19
String text = readLine(Enter text to check: );
20
text = removeSpaces(text);
21
if ( isPalindrome(text) ) {
22
println(Text is palindrome.);
23
} else {
24
println(Text is not palindrome.);
25
}
26 }
27 }
Programacin 2
Curso 2011/2012
Programacin 2
Curso 2011/2012
Programacin 2
Curso 2011/2012
19
"abcba"
"abcba"
removeExtrems
text: "abcba"
"abcba"
"abcba"
"Text is pal"
true
isPalindrome
21
println
22
text: "abcba"
rst: 'a'
last: 'a'
inner: "bcb"
20
true
"bcb"
isPalindrome
8
text: "bcb"
rst: 'b'
last: 'b'
inner: "c"
"c"
true
isPalindrome
8
text: "c"
Resumiendo
Intentemos resumir en una tabla las intuiciones que
obtenemos pensando en la imagen de las muecas rusas y
su equivalente en cuanto a la solucin recursiva de un
problema5:
Muecas rusas
Solucin recursiva
Una mueca puede abrirse
Un problema de
para ver qu es lo que hay
descompone en varios
en su interior.
subproblemas .
necesidad de ir generando un String diferente para cada
llamada recursiva.
5 Como toda metfora la coincidencia no es exacta, pero
puede ayudarnos a tener intuiciones.
J.M. Gimeno y J.L. Gonzlez
Programacin 2
Curso 2011/2012
Al descomponer un
problema grande (casos
recursivos) encontramos
subproblemas que tienen la
misma estructura que el
problema inicial y trabajan
sobre datos ms pequeos
La mueca ms pequea ya Existen casos simples cuya
no contiene otras muecas
solucin no requiere
descomponerlos ms.
Slo hay dos tipos de
Entre los casos simples y los
muecas (las que contienen recursivos tengo todas las
otras en su interior y las ms posibilidades cubiertas
pequeas que no las
contienen).
10
Programacin 2
Curso 2011/2012
rst
last
11
Programacin 2
Curso 2011/2012
begin
end
Vectores palndromos
La funcin recursiva que realizaremos tendr la siguiente
forma:
1 public boolean isPalindromeArray(char[] textChars,
2
int begin,
3
int end) {
4
5 // Checks whether the subarray from begin to
6 // end-1 of textChars is palindrome
7}
12
Programacin 2
Curso 2011/2012
13
Programacin 2
Curso 2011/2012
L
end
0
begin
pos
pos
Si es el extremo derecho:
14
Programacin 2
Curso 2011/2012
15
Programacin 2
Curso 2011/2012
Solucin iterativa
16
Programacin 2
Curso 2011/2012
17
Programacin 2
Curso 2011/2012
b div 2)+1
2
Si aplicamos el anlisis anterior a la frmula ab , tenemos:
b div 2)
Si b es par,
2
a b=a
b div 2)+1
Si b es impar,
2
b
a =a
Es decir, en ambos casos, para calcular ab hemos de
calcular ( a2 ) b div 2 , tan slo que en caso de que b sea impar,
18
Programacin 2
uno que va desde la
solucin del caso simple
hasta regresar de la
llamada inicial y que va
propagando la solucin
Fijaos que conseguimos
estos dos bucles sin
explicitarlos de manera
alguna (con un while, o un
for).
Curso 2011/2012
7
13
7 * 13841287201
= 96889010407
exp2(7, 13)
7*7 = 49
13/2 = 6
13841287201
exp2(49, 6)
49*49 = 2401
6/2=3
2401 * 576801
= 13841287201
exp2(2401, 3)
un algoritmo
recursivo
2401 * 2401
= 5764801
5764801 * 1
19
Programacin 2
Curso 2011/2012
20
Programacin 2
Curso 2011/2012
Reformular el problema
21
Programacin 2
Curso 2011/2012
x <r x r
Anlogamente por la segunda rama
r x < r< x +
que, junto con la condicin de estar en este caso ( x<r ),
hace en este caso se deba cumplir:
x<r < x + r ( x , x+ )
Juntando las condiciones de ambas ramas, se tiene:
r ( x , x +)
Grficamente, para que r se considere un resultado
suficientemente bueno ha de estar en el siguiente intervalo:
22
Programacin 2
Curso 2011/2012
Reformulemos el problema
Supongamos que nos dan como pista un intervalo,
delimitado por a y b , en el que nos dicen que ah
dentro est x . Es decir,
x [ a , b ]
Dicha condicin es equivalente a:
2
x [a , b ]
El intervalo es pequeo
23
Programacin 2
Curso 2011/2012
El intervalo es grande
Si el intervalo es grande, la idea es descomponerlo en
intervalos ms pequeos y preguntarnos en cual de ellos
estar x para que sea la llamada recursiva quin nos
resuelva el problema (recordad que solamente podemos
llamar a la funcin usando un intervalo que contenga el
valor de raz de x).
24
Programacin 2
Curso 2011/2012
La llamada inicial
En resumen:
1 public double squareRoot(double x, double epsilon) {
25 return squareRoot(x, epsilon, 0.0, Math.max(1.0, x));
26 }
Dos comentarios:
el uso de la funcin Math.max, para decidir si usamos
un valor u otro para (tambin se podra usar un
condicional pero usar max o min en estos casos es
habitual).
25
Programacin 2
Curso 2011/2012
26
Programacin 2
Curso 2011/2012
6. Seno de un ngulo
Otro ejemplo de programa recursivo sobre nmeros en
coma flotante se puede plantear para el clculo del seno de
un ngulo a partir de:
1. Frmula del seno del ngulo triple
( )
sin
=1
27
Programacin 2
Curso 2011/2012
28
Programacin 2
Curso 2011/2012
29
Programacin 2
Curso 2011/2012
>x
pos
Casos particulares:
si x pertenece al vector, ocupar la posicin pos
si x aparece varias veces en el vector, se dar la
posicin de ms a la derecha
si todos los elementos del vector son x , el valor
devuelto es -1
17 Tambin denominada bsqueda dicotmica.
J.M. Gimeno y J.L. Gonzlez
30
Programacin 2
Curso 2011/2012
>x
left
right
31
Programacin 2
Curso 2011/2012
Caso simple
>x
pos
right
left
Caso recursivo
mid=()div 2 ;
Grficamente:
x
>x
left
mid
right
mid
>x
left right
32
Programacin 2
Curso 2011/2012
>x
left
mid
right
33
Programacin 2
Curso 2011/2012
( mid+1 )
( mid+1 ) mid+ 1 2 Lo cual es cierto para
mid
>
ya
que
div 2
34
Programacin 2
Curso 2011/2012
Caso v[mid]>x:
Ahora se trata de probar que
= 2> 2
>
ya que
35
Programacin 2
Curso 2011/2012
"grandes"
36
Programacin 2
Curso 2011/2012
left
right
El problema de la particin
left
pos
right
37
Programacin 2
Curso 2011/2012
Es decir:
left
>p
inf
sup
right
En la que
el caso simple ser cuando la zona intermedia est
vaca (inf==sup) y el valor a retornar ser inf
la llamada inicial se realizar con inf==left y
sup==right.
En el caso recursivo, la estrategia es reorganizar todo el
subvector de en medio en base a reorganizar un subvector
menor. Adems, para que la llamada recursiva se realice
sobre un subvector de menor tamao, o bien ha de crecer
la zona de la izquierda, o la de la derecha, o ambas.
Para que crezca la zona de la izquierda, ha de pasar
que v[inf] <=p. En este caso, puedo reorganizar el
vector con la llamada recursiva sobre inf+1 y sup.
Para que crezca la zona de la derecha, anlogamente,
se ha de cumplir que v[sup-1]>p. En este caso, puedo
reorganizar el vector con la llamada inf, sup-1.
Qu pasa si no se cumple ningn caso de los
anteriores? Fijaos en que esto se produce cuando
v[inf]>p && v[sup-1]<=p.
En este caso puedo intercambiar los elementos de las
posiciones inf y sup-1 del vector v y reorganizar
recursivamente el subvector delimitado por inf+1 y
sup-1.
En Java, quedara:
38
Programacin 2
Curso 2011/2012
Unindolo todo
Usando la funcin de particin dentro de quickSort y
suponiendo que existe una funcin que, dado un subvector,
nos selecciona un elemento de l para usar como pivote,
nos queda:
39
Programacin 2
Curso 2011/2012
right
right
40
Programacin 2
Curso 2011/2012
>p
p
p
pos
left
right
p
p
left
pos
right
Consideraciones finales
En nuestro caso hemos diseado la funcin partition
como una funcin recursiva. Por cuestiones de
eficiencia casi siempre la veris implementada
iterativamente.
J.M. Gimeno y J.L. Gonzlez
41
Programacin 2
Curso 2011/2012
42
Programacin 2
Curso 2011/2012
Planteamiento recursivo
Una forma de encontrar la solucin consiste en estudiar las
condiciones bajo las cuales puedo mover el disco ms
grande:
43
Programacin 2
Curso 2011/2012
Casos simples
44
Programacin 2
Curso 2011/2012
A->C
C->B
A->B
B->C
C->A
A->B
45
Programacin 2
Curso 2011/2012
Simplificando la solucin
Recursividad mltiple
46
Programacin 2
Curso 2011/2012
47
Programacin 2
Curso 2011/2012
10.
Nmero de particiones de un
nmero natural
Un conjunto de nmeros naturales >0 es una particin de
un nmero dado n, si la suma de dichos nmeros es n. Es
decir:
48
Programacin 2
Curso 2011/2012
Probamos?
#(2) = 1 + #(1)*#(1) = 1+1*1 = 2
#(3) = 1 + #(1)*#(2) + #(2)*#(1) = 1 + 1*2 + 2*1 =
5
Pero 3 se descompone como {3}, {2,1}, {1,1,1}, es decir,
de 3 formas diferentes.
Segunda aproximacin
Para evitar repeticiones, si ya hemos considerado la
descomposicin de 3 como 2+1 ya no consideraremos la
1+2, es decir:
n2
Probamos?
#(2) = 1 + #(1)*#(1) = 1 + 1*1 = 2
#(3) = 1 + #(1)*#(2) = 1 + 1*2 = 3
#(4) = 1 + #(1)*#(3) + #(2)*#(2) = 1*3 + 2*2 = 7
Pero 4 se descompone en, {4}, {3,1},{2,2}, {2,1,1},
{1,1,1,1}. El problema es que la {1,1,1,1} y {2,2} se
cuentan dos veces.
J.M. Gimeno y J.L. Gonzlez
49
Programacin 2
Curso 2011/2012
( n )= (n , k )
k=1
o tambin
n= {d 1 , ,d k } nd k = { d 1 , , d k1 }
50
Programacin 2
Curso 2011/2012
d
( k + d)
d1 , ,
n=
o tambin
d
( k d)
d1, ,
n= {d 1 , ,d k } nd=
n= { d 1 , ,d k } nkd=
51
Programacin 2
( n , k )=
Curso 2011/2012
0 k >n
1 k =n
(n1, k 1)+ ( nk , k ) k < n
52
Programacin 2
11.
Curso 2011/2012
Bibliografa
53