Está en la página 1de 55

Curso 2021/22

Introducción a la programación

Tema 4. Introducción a la recursividad

Oriol Borrás Gené @oriolTIC


Índice:

Tema 4. 4.1. Conceptos básicos

Introducción a la 4.2. Recursividad lineal

recursividad 4.3. Recursividad múltiple

4.4. Recursividad mutua


4.1. Conceptos básicos

Ejercicio. Calcular el factorial de un número entero n.

Directa
n=4
n! = n*(n-1)*(n-2)* … 2*1*1 4! = 4*3*2*1*1 = 24

Recursiva 4! = 4*3! n>0


3! = 3*2! = 6
n*(n-1)! si n>0
n! =
n=4 2! = 2*1! = 2
1! = 1*0! = 1
1 si n=0
0! = 1 n=0

Oriol Borrás Gené @OriolTIC


4.1. Conceptos básicos

Recurrencia y recursividad.

● Una función aparece en su propia definición.


● Un problema se descompone en subproblemas del mismo tipo.
● Alternativa al uso de bucles.

En Pascal:

● Subprogramas recursivos.
● En el cuerpo del subprograma aparece una llamada a sí mismo.
● No tiene ninguna sintaxis especial.

Oriol Borrás Gené @OriolTIC


4.1. Conceptos básicos

Proceso de llamada

● Se reserva espacio de memoria para almacenar los parámetros y


demás objetos locales del subprograma.
● Se reciben los parámetros y se cede el control de ejecución al
subprograma que comienza a ejecutarse.
● Cuando termina la ejecución, se libera el espacio reservado, los
identificadores locales dejan de tener vigencia y se ejecuta la siguiente
instrucción a la llamada.
● El proceso se repetirá hasta que se llegue al caso base, el cual
devolverá un resultado o no dará lugar a otra llamada recursiva. Todos
el resto de casos previos se denominan casos recurrentes.

Oriol Borrás Gené @OriolTIC


4.1. Conceptos básicos

Proceso de llamada

Subprograma
PROCEDURE Pr1 (...);

BEGIN Subprograma
… PROCEDURE Pr1 (...);
Pr1(...); …
… BEGIN Subprograma
END; …
PROCEDURE Pr1 (...);
Pr1(...); Subprograma


BEGIN
END; PROCEDURE Pr1 (...);


Pr1(...);
BEGIN

END;

END;

CASOS RECURRENTES CASO BASE

Oriol Borrás Gené @OriolTIC


4.1. Conceptos básicos

Proceso de llamada (ejemplo factorial)

Teniendo la función FacRec (n):

FacRec (4) = 4 * FacRec (3)


FacRec (3) = 3 * FacRec (2) Llamadas recurrentes a la
función FacRec
FacRec (2) = 2 * FacRec (1) (CASOS RECURRENTES)

FacRec (1) = 1 * FacRec (0)


FacRec (0) ⇒1 CASO BASE

FacRec (1) ⇒1*1 ⇒ 1


FacRec (2) ⇒2*1 ⇒ 2 Se ejecutan las
distintas llamadas a
FacRec (3) ⇒3*2 ⇒ 6 la función FacRec

FacRec (4)⇒4*6 ⇒ 24
Oriol Borrás Gené @OriolTIC
4.1. Conceptos básicos

Partes de un subprograma recursivo

● Caso base:
○ Dados los parámetros de entrada, la solución del problema es
“simple”.
○ No se generan llamadas recursivas, y se devuelve directamente una
solución.
Ejemplo: 0! = 1

● Caso recurrente:
○ Caso más complejo: no hay solución trivial.
○ Se reduce a otro caso más simple.
Ejemplo: 4! = 4*3!

Oriol Borrás Gené @OriolTIC


4.1. Conceptos básicos

Ejercicio 1a. Escribir un subprograma que calcule el factorial de un


número entero mediante iteraciones.

¿Procedimiento o función?

¿Qué instrucción de iteración?

Ejercicio 1b. Escribir un subprograma que calcule el factorial de un


número entero mediante recursividad.

n*(n-1)! si n>0
n! =
1 si n=0
Oriol Borrás Gené @OriolTIC
4.1. Conceptos básicos

Errores comunes: recursión infinita

● Se produce una sucesión infinita de llamadas.


● El control pasa siempre al caso recurrente, nunca se llega al caso base.
● Ejemplo FacRec(-1).

Consejos para evitar la recursión infinita:

○ Usar una estructura de selección (IF o CASE), para distinguir entre caso base
y caso recurrente.
○ Asegurar que los parámetros de la llamada recursiva sean diferentes de los
de entrada (condición necesaria para que “se acerquen” al caso base).
○ No olvidar asignar el resultado de la función recursiva a su nombre.
○ En los programas recursivos sencillos, no suele ser necesario usar bucles.

factorial(num-1) ≠ factorial (num)


Oriol Borrás Gené @OriolTIC
4.2. Recursividad lineal

Cada llamada recursiva genera como máximo otra nueva llamada


recursiva.
Ejemplos:
• Cálculo recursivo del factorial: FacRec.
• Versión recursiva del algoritmo de suma lenta: SumaLentaRec.

Oriol Borrás Gené @OriolTIC


4.2. Recursividad lineal

Ejercicio 2a. Diseñar y codificar un programa en Pascal que


calcule la suma lenta de dos números introducidos por un usuario.
En primer lugar no se utilizará recursividad.

Suma lenta: ¿Procedimiento o función?

Oriol Borrás Gené @OriolTIC


4.2. Recursividad lineal

Ejercicio 2b. Diseñar y codificar un programa en Pascal que


calcule la suma lenta de dos números introducidos por un usuario.
Utilizando recursividad.

Suma lenta:

Oriol Borrás Gené @OriolTIC


4.2. Recursividad lineal

Ejercicio 3. Escribir un
subprograma recursivo que
escriba los dígitos de un
número entero en orden
inverso. Utilizar recursividad.

Oriol Borrás Gené @OriolTIC


4.2. Recursividad lineal

Ejercicio 3. Escribir un subprograma recursivo que escriba los dígitos de un número


entero en orden inverso. Utilizar recursividad.

PROGRAM invertirnum;
VAR
numero :integer;

FUNCTION invertir (num,m:integer): integer;

Oriol Borrás Gené @OriolTIC


4.2. Recursividad lineal

Ejercicio 3. Escribir un subprograma recursivo que escriba los dígitos de un número


entero en orden inverso. Utilizar recursividad.

PROGRAM invertirnum; En num está el número


VAR sin invertir y en m
numero :integer; vamos guardando el
invertido.
FUNCTION invertir (num,m:integer): integer;
BEGIN
IF (num = 0) THEN
invertir:=m;
ELSE
invertir:=invertir (num DIV 10, m*10 + num MOD 10);
END;
BEGIN
writeln ('Escribe un número igual o mayor que cero');
readln(numero);
writeln (invertir(numero,0));
END.

Oriol Borrás Gené @OriolTIC


4.2. Recursividad lineal

Ejemplo:

numero = 2456
4.2. Recursividad lineal

numero = 2456
num = ?
m=?
2 invertir = ?

Oriol Borrás Gené @OriolTIC


4.2. Recursividad lineal

numero = 2456
num = 2456
m=0
2 invertir = ?
3

4
5
num = 245
invertir (245,0*10+6) 6 m=6
1 invertir = ?
invertir (245,6)
7
8 invertir (24,6*10+5)
num = 24 13
9
m = 65 invertir (24,65) num = 0
14 m = 6542
invertir = ?
15 invertir = ?
10
invertir (0,6542)
10
num = 2
invertir (2,65*10+4) 11 num = 0
m = 654 m = 6542
invertir = ? invertir = 6542
invertir (2,654) 12
4.2. Recursividad lineal

numero = 2456
num = 2456
m=0
invertir = ?

23
24 invertir = 6542

25
21
invertir = 6542
22

15
19
20 invertir = 6542
16

num = 0
m = 6542
invertir = 6542

invertir = 6542 17
18
4.2. Recursividad lineal

Recursividad por la cola.


● Caso especial de la recursividad lineal.
● No se realizan operaciones con el resultado que devuelve una
llamada recursiva, a diferencia del caso del factorial que no sería
recursividad por la cola pues se multiplica por num:
factorial := num * factorial(num-1);

● El resultado es el que devuelve la última llamada.


● Ejemplos: suma lenta e invertir entero.
sumalenta := sumalenta (a-1,b+1)
invertir := invertir(num DIV 10, m * 10 + num MOD 10);

Oriol Borrás Gené @OriolTIC


4.3. Recursividad múltiple

Alguna llamada genera dos o más nuevas llamadas recursivas.

Ejemplos:
● Números de Fibonacci, polinomio de Hermite, etc.
● Algoritmo recursivo para las Torres de Hanoi.

Oriol Borrás Gené @OriolTIC


4.3. Recursividad múltiple

Ejercicio 3. Sucesión de Fibonacci.

Fibn = Fib(n-2) + Fib(n-1)


(fibi)i∈N = 0,1,1,2,3,5,8,13,21,34,…

CASO BASE
Fib0 = 0
Fib1 = 1
Fib2 = Fib0 + Fib1 CASO RECURSIVO

Fib3 = Fib1 + Fib2



https://es.wikipedia.org/wiki/Sucesi%C3%B3n_de_Fibonacci

Oriol Borrás Gené @OriolTIC


Fibonacci: Llamadas repetidas
• Ejemplo: fib(5) Fibn = Fib(n-2) + Fib(n-1)
5

4 3

3 2 2 1

2 1 1 0 1 0

1 0

Oriol Borrás Gené @OriolTIC


4.3. Recursividad múltiple

Torres de Hanoi
Juego de sencilla solución recursiva.
Situación inicial:
• 3 agujas verticales A, B y C
• En una de ellas hay n discos de tamaño creciente.
A B C

n=4

Oriol Borrás Gené @OriolTIC


4.3. Recursividad múltiple

Torres de Hanoi
Objetivo: Pasar los n discos en el mismo orden a otra aguja.
Restricciones:
• Los discos se pasan de uno en uno.
• Un disco NUNCA debe descansar sobre otro de menor tamaño.
A B C

n=4

Oriol Borrás Gené @OriolTIC


4.3. Recursividad múltiple

Torres de Hanoi
Objetivo: Pasar los n discos en el mismo orden a otra aguja.
Restricciones:
• Los discos se pasan de uno en uno.
• Un disco NUNCA debe descansar sobre otro de menor tamaño.
A B C

n=4

Oriol Borrás Gené @OriolTIC


X
4.3. Recursividad múltiple

Torres de Hanoi
Algoritmo.
● Caso n=1:
○ Pasar 1 disco de A → B (trivial)

A B C A B C

Oriol Borrás Gené @OriolTIC


4.3. Recursividad múltiple (Pascal)

Torres de Hanoi (n=1) n=1


num = ?
origen = ?
destino = ?
auxiliar = ?

Desde el programa principal se llamará al procedimiento:


n:=1; 1

hanoi (n, ’A’, ‘B’, ‘C’); 2

Oriol Borrás Gené @OriolTIC


>>

4.3. Recursividad múltiple (Pascal)


Torres de Hanoi (n=1)
num= 1
Parámetros ficticios: origen= ‘A’
1 2 3 4 destino = ‘B’
3 auxiliar = ‘C’
4

5 hanoi (1-1, ‘A’ , ‘C’, ‘B’);


8

num= 0
origen= ‘A’
destino = ‘C’
auxiliar = ‘B’
7

Oriol Borrás Gené @OriolTIC


>> Pasar disco 1 de
A a B.
4.3. Recursividad múltiple (Pascal)
Torres de Hanoi (n=1)
num= 1
origen= ‘A’
destino = ‘B’
auxiliar = ‘C’

hanoi (1-1, ‘C’ , ‘B’, ‘A’);


8
9

12 10

num= 0
origen= ‘C’
destino = ‘B’
auxiliar = ‘A’
11

Oriol Borrás Gené @OriolTIC


4.3. Recursividad múltiple (Pascal)

Torres de Hanoi (n=2) n=2


num = ?
origen = ?
destino = ?
auxiliar = ?

Desde el programa principal se llamará al procedimiento:


n:=2; 1

hanoi (n, ’A’, ‘B’, ‘C’); 2

Oriol Borrás Gené @OriolTIC


>>

4.3. Recursividad múltiple (Pascal)


Torres de Hanoi (n=2)
num= 2
origen= ‘A’
destino = ‘B’
3 auxiliar = ‘C’
4
num= 1
hanoi (2-1, ‘A’ , ‘C’, ‘B’); origen= ‘A’
5 destino = ‘C’
auxiliar = ‘B’

6
num= 0
origen= ‘A’
7
destino = ‘B’ 10
auxiliar = ‘C’ hanoi (1-1, ‘A’ , ‘B’, ‘C’);

9
>> Pasar disco 1 de
AaC
4.3. Recursividad múltiple (Pascal)
Torres de Hanoi (n=2)
num= 2
origen= ‘A’
destino = ‘B’
3 auxiliar = ‘C’
4
num= 1
hanoi (2-1, ‘A’ , ‘C’, ‘B’); origen= ‘A’
5 destino = ‘C’
auxiliar = ‘B’

6
num= 0
origen= ‘B’
7
destino = ‘B’ 10
auxiliar = ‘A’ hanoi (1-1, ‘B’ , ‘C’, ‘A’); 11

12

13
>> Pasar disco 1 de
AaC
4.3. Recursividad múltiple (Pascal)
Torres de Hanoi (n=2)
num= 2
origen= ‘A’
destino = ‘B’
3 auxiliar = ‘C’
4
num= 1
hanoi (2-1, ‘A’ , ‘C’, ‘B’); origen= ‘A’
5 destino = ‘C’
14 auxiliar = ‘B’

6
num= 0
origen= ‘B’
7
destino = ‘B’ 10
auxiliar = ‘A’ hanoi (1-1, ‘B’ , ‘C’, ‘A’); 11

12

13
>> Pasar disco 1 de
AaC
4.3. Recursividad múltiple (Pascal) >> Pasar disco 2 de
AaB

Torres de Hanoi (n=2)


num= 2
origen= ‘A’
destino = ‘B’
3 auxiliar = ‘C’
4
num= 1
hanoi (2-1, ‘C’ , ‘B’, ‘A’); origen= ‘C’
5 destino = ‘B’
14 auxiliar = ‘A’
15

16
num= 0
origen= ‘C’
17
destino = ‘A’ 20
auxiliar = ‘B’ hanoi (1-1, ‘C’ , ‘A’, ‘B’);

18

19
>> Pasar disco 1 de
AaC
4.3. Recursividad múltiple (Pascal) >> Pasar disco 2 de
AaB
>> Pasar disco 1 de
CaB
Torres de Hanoi (n=2)
num= 2
origen= ‘A’
destino = ‘B’
3 auxiliar = ‘C’
4
num= 1
hanoi (2-1, ‘C’ , ‘B’, ‘A’); origen= ‘C’
5 destino = ‘B’
13 auxiliar = ‘A’
14

24 16
num= 0
origen= ‘A’
17
destino = ‘B’ 20
auxiliar = ‘C’ hanoi (1-1, ‘A’ , ‘B’, ‘C’); 21

22

23
4.3. Recursividad múltiple

Torres de Hanoi
A B C
Algoritmo.
● Caso n=2 (pasar 2 discos de A a B):
○ Mover disco de A → C
○ Mover disco de A → B Punto de partida
○ Mover disco de C → B

A B C A B C A B C

Paso 1 Paso 2 Paso 3


Oriol Borrás Gené @OriolTIC
4.3. Recursividad múltiple

A B C
Torres de Hanoi A B C
Algoritmo.
● Caso n=3 (pasar 3 discos de A → B) Paso 1
1. 2 discos de A → C:
a. mover A → B A B C
b. mover A → C
c. mover B → C
Paso 2
2. 1 disco de A → B
3. 2 discos de C → B: A B C
a. mover C → A
mover C → B
mover A → B Paso 3
Oriol Borrás Gené @OriolTIC
4.3. Recursividad múltiple

Torres de Hanoi
Algoritmo.
● Caso general (pasar n discos de A → B):
○ Pasar n-1 discos de A → C
○ Mover disco de A → B
○ Pasar n-1 discos de C → B

Oriol Borrás Gené @OriolTIC


4.3. Recursividad múltiple

Torres de Hanoi
Codigo en Pascal

PROCEDURE hanoi (num:integer;origen, destino, auxiliar:char);


BEGIN
IF num>0 THEN
BEGIN
hanoi (num-1, origen, auxiliar, destino);
writeln('Pasar disco ',num,' de ', origen, ' a ', destino);
hanoi (num-1, auxiliar, destino, origen);
END
END;

Oriol Borrás Gené @OriolTIC


4.3. Recursividad múltiple

Torres de Hanoi
Traza

Isidoro Hernán

Oriol Borrás Gené @OriolTIC


4.3. Recursividad múltiple

Torres de Hanoi
Traza (n=4) 1. Se pasa el disco 1 de A a C
2. Se pasa el disco 2 de A a B
Llamada hanoi (4,A,B,C): 3. Se pasa el disco 1 de C a B
4. Se pasa el disco 3 de A a C
5. Se pasa el disco 1 de B a A
6. Se pasa el disco 2 de B a C
7. Se pasa el disco 1 de A a C
8. Se pasa el disco 4 de A a B
9. Se pasa el disco 1 de C a B
10. Se pasa el disco 2 de C a A
11. Se pasa el disco 1 de B a A
12. Se pasa el disco 3 de C a B
13. Se pasa el disco 1 de A a C
14. Se pasa el disco 2 de A a B
15. Se pasa el disco 1 de C a B
Oriol Borrás Gené @OriolTIC
4.3. Recursividad múltiple

Torres de Hanoi
Traza (n=4)
Llamada hanoi (4,A,B,C):

hanoi (4,’A’,’B’,’C’)
hanoi (3,’A’,’C’,’B’)
hanoi (2,’A’,’B’,’C’)
hanoi (1,’A’,’C’,’B’)
hanoi (0,’A’,’B’,’C’)
Se pasa el disco 1 de A a C
hanoi (0,’B’,’C’,’A’)
Se pasa el disco 2 de A a B
hanoi (1,’C’,’B’,’A’)
hanoi (0,’ ...)
Se pasa el disco 1 de C a B ...
Oriol Borrás Gené @OriolTIC
4.3. Recursividad mutua

● Recursión simple (directa): un subprograma llama a sí mismo.

● Recursión mutua (indirecta): definición de dos o más


subprogramas basándose recíprocamente en ellos mismos.

La recursividad en el subprograma se produce indirectamente.


Un subprograma A llama a B, y B llama (directamente o
indirectamente) a A.

Oriol Borrás Gené @OriolTIC


4.3. Recursividad mutua

● Recursión simple (directa):


○ Un subprograma llama a sí mismo.

● Recursión mutua (indirecta):


○ Definición de dos o más subprogramas basándose recíprocamente
en ellos mismos.
○ La recursividad en el subprograma se produce indirectamente.
○ Un subprograma A llama a B, y B llama (directamente o
indirectamente) a A.

Oriol Borrás Gené @OriolTIC


4.3. Recursividad mutua

● Se aplica en Pascal mediante subprogramas.


● En Pascal un identificador es conocido sólo después de su
declaración.
● El problema es que al menos un subprograma ha de llamar a otro,
antes de que éste último sea declarado.

Sintaxis:
● Predeclaración del segundo subprograma con la palabra reservada
FORWARD.

Oriol Borrás Gené @OriolTIC


4.3. Recursividad mutua

Sintaxis:

PROCEDURE B (parámetros); FORWARD {Predeclaración de B}

PROCEDURE A (parámetros); {Declaración de A}


BEGIN {A}
B();
....
END; {A}

PROCEDURE B (parámetros); {Declaración de B}


BEGIN {B}
A():
...
END; {B}
...

Oriol Borrás Gené @OriolTIC


4.3. Recursividad mutua

Ejemplo: Escribir funciones para determinar la paridad de un número


positivo utilizando recursión mutua.

● EsPar(n):
○ TRUE si n=0
○ EsImpar(n–1) si n>0
● EsImpar (n):
○ FALSE si n=0
○ EsPar(n–1) si n>0

Oriol Borrás Gené @OriolTIC


4.3. Recursividad mutua

PROGRAM esParImpar;
Ejemplo: Escribir funciones para VAR n: integer;

determinar la paridad de un FUNCTION esImpar (n:integer):boolean; FORWARD;


FUNCTION esPar (n:integer):boolean;

número positivo utilizando BEGIN


IF n = 0 THEN
recursión mutua. esPar:=true {caso base}
ELSE
esPar:=esImpar(n-1) {caso recurrente}
END
FUNCTION esImpar (n:integer):boolean;
BEGIN
IF n = 0 THEN
esImpar:=false {caso base}
ELSE
esImpar:=esPar(n-1) {caso recurrente}
END
BEGIN {programa principal}
XXX
END.

Oriol Borrás Gené @OriolTIC


Iteración y recursión

Repetición de bloques e instrucciones:

● Iteración:
○ Bucles (WHILE, REPEAT, FOR).

● Recursión:
○ Subprogramas que se llaman a sí mismos.

Oriol Borrás Gené @OriolTIC


Iteración y recursión

Repetición de bloques e instrucciones:

● Equivalencia de Iteración y Recursión: cualquier cómputo


recursivo puede expresarse de forma iterativa y viceversa.

● Ejemplos:
○ Factorial: fac y facRec.
○ Suma lenta: sumaLenta y sumaLentaRec.
○ Nº de Fibonacci: fib y fibIter.

Oriol Borrás Gené @OriolTIC


Claridad Vs. Eficiencia

● Claridad:
○ Muchos problemas se resuelven de forma “elegante”
mediante recursión, requiriendo programas complejos y/o
poco intuitivos en su versión iterativa.
Ejemplo: las Torres de Hanoi.

● Eficiencia:
○ Hay que tener en cuenta también la complejidad añadida
por la recursión.
Ejemplo: los números de Fibonacci.

Oriol Borrás Gené @OriolTIC


Recomendaciones técnicas

Utilizar recursividad:

● Cuando clarifique el algoritmo y el programa que soluciona un


problema.
● Cuando no haya fuertes restricciones de memoria o tiempo de
ejecución.

Evitar la recursión mutua:

● El grafo de llamadas puede resultar difícil de entender.


● Preferir una estructura jerárquica de llamadas.

Oriol Borrás Gené @OriolTIC


Recomendaciones técnicas

Al hora de aplicar la recursividad (error SIGSEGV)

● Las modificaciones de los parámetros se


realizan en la propia llamada recursiva, ej:
FUNCTION recur (num1,num2:integer):integer; FUNCTION recur (num1,num2:integer):integer;
BEGIN BEGIN
IF num1 = 0 THEN IF num1 = 0 THEN
recur:=0; recur:=0;
ELSE ELSE
num1:=num1+1; recur:=recur(num1+1,num2 DIV 2);
num2:= num2 DIV 2; END;
recur:=recur(num1,num2);
END;

● Las modificaciones asociadas a la llamada recursiva también,


ej:
recur:=num1+recur(num1,num2 DIV 2);
Oriol Borrás Gené @OriolTIC

También podría gustarte