Recursividad
Estructura de archivos:
📁 02_Recursividad
└── 📁 Recursividad-NN
├── 📄 main.c
└── 📄 Makefile
Ejercicios:
Recursividad 01
Makefile
CC = gcc
CFLAGS = -std=c11 -Wall -Wextra -pedantic
PROG = main
OBJ = main.o
$(PROG): $(OBJ)
$(CC) $(OBJ) -o $(PROG)
$(OBJ): main.c
$(CC) $(CFLAGS) -c main.c
.PHONY: clean
clean:
del $(OBJ) $(PROG).exe
Código:
// gcc -std=c11 -Wall -Wextra -pedantic main.c -o main
/*
Recursion infinita
- no existe caso base (condicion de corte), por lo tanto
el problema no se resolvera, provocando un desbordamiento de la pila (stack overflow)
El compilador nos advierte:
- infinite recursion detected [-Winfinite-recursion]
- note: f(i + 1) recursive call
*/
#include <stdio.h>
int f(int);
int main(void)
{
int i;
i = f(3);
printf("\ni = %d\n", i);
return 0;
}
// Función recursiva sin caso base. Carece de condición de corte.
int f(int i)
{
printf("\ni = %d\n", i);
i = f(i+1); // Caso recursivo
return i;
}
Recursividad 02
Makefile
CC = gcc
CFLAGS = -std=c11 -Wall -Wextra -pedantic
PROG = main
OBJ = main.o
$(PROG): $(OBJ)
$(CC) $(OBJ) -o $(PROG)
$(OBJ): main.c
$(CC) $(CFLAGS) -c main.c
.PHONY: clean
clean:
del $(OBJ) $(PROG).exe
Código:
// gcc -std=c11 -Wall -Wextra -pedantic main.c -o main
/*
Caso base mal hecho
*/
#include <stdio.h>
/* Declaracion variable global.
* Tiene almacenamiento estatico.
* Se inicializa en 0 si el usuario no ingresa nada válido. */
int g;
/* Prototipo de funcion recursiva */
int fun(int);
int main(void)
{
/* Declaracion de variable entera y asignacion de un entero.
* Vive en el stack de main */
int i;
i = 5;
/* Se lee un valor para la variable global g, pero no es utilizada. */
scanf("%d", &g);
/* Asigna a la variable lo que devuelva la funcion recursiva, pasando como argumento (5 * 3).
* Esta es la primera llamada recursiva. */
i = fun(i * 3);
/* Mostrar el valor de la variable i*/
printf("\n%d\n", i);
return 0;
}
/* Definicion de la funcion recursiva
* Recibe como argumento una variable entera.
* Cada llamada crea:
* - Su propia copia de j.
* - Su propia variable entera k. La cual retorna. */
int fun(int j)
{
int k;
j++;
/* Caso recursivo si j es menor que 20, se vuelve a llamar a fun*/
if (j < 20)
k = fun(j);
/* Problema grave:
* Si j es mayor o igual a 20, k queda sin inicializar
* se retorna basura, comportamiento indefinido (UB).
* No existe un caso base explicito con return definido.
* TODA funcion recursiva debe tene un caso base claro que retorne un valor valido. */
return k;
}
Recursividad 03
Makefile
CC = gcc
CFLAGS = -std=c11 -Wall -Wextra -pedantic
PROG = main
OBJ = main.o
$(PROG): $(OBJ)
$(CC) $(OBJ) -o $(PROG)
$(OBJ): main.c
$(CC) $(CFLAGS) -c main.c
.PHONY: clean
clean:
del $(OBJ) $(PROG).exe
Código:
// gcc -std=c11 -Wall -Wextra -pedantic main.c -o main
/*
Recursion correcta (factorial)
*/
#include <stdio.h>
/* Prototipo de funcion recursiva */
long int fact(int);
int main(void)
{
/* Declaracion de variables */
int x;
long int f;
/* Lectura de valor de x, sin validacion de la entrada del usuario */
scanf("%d", &x);
/*Asignacion de variable long int,
* De lo que devuelva la llamada a la funcion,
* a la que se le pasa el valor de x leido anteiormente.
* Primer llamada a funcion recursiva. */
f = fact(x);
/* Mostrar el valor ingresado a la funcion y el valor devuelto por ella, usando los especificadores correspondientes:
* %d para int.
* %ld para long int. */
printf("\n%d\t%ld", x, f);
return 0;
}
/* Declaracion de la funcion recursiva:
* Recibe parametro por valor de tipo int (Cada llamada tiene su propia copia).
* Devuelve F de tipo long int. */
long int fact(int n)
{
/* Declaracion variable local y asignacion. Caso base implicito.
* Si n es menor e igual a 1 el bucle no se cumple pero la funcion devuelve F = 1. */
long int F = 1;
/* Caso recursivo:
* Si n es mayor que 1
* Se multiplica n por el factorial de (n - 1).*/
if (n > 1)
F = n * fact(n - 1);
return F;
}
Recursividad 04
Makefile
CC = gcc
CFLAGS = -std=c11 -Wall -Wextra -pedantic
PROG = main
OBJ = main.o
$(PROG): $(OBJ)
$(CC) $(OBJ) -o $(PROG)
$(OBJ): main.c
$(CC) $(CFLAGS) -c main.c
.PHONY: clean
clean:
del $(OBJ) $(PROG).exe
Código:
// gcc -std=c11 -Wall -Wextra -pedantic main.c -o main
/*
Recursion correcta pero ineficiente (fibonacci)
*/
#include <stdio.h>
/* Prototipo de la funcion recursiva Fibonacci*/
int fibo(int);
int main(void)
{
/* Declaracion de variables enteras sin inicializar */
int x, f;
/* Lectura del valor de x sin validacion */
scanf("%d", &x);
/* Primer llamada recursiva, pasado el argumento x
* y almacenado su retorno en la variable entera f. */
f = fibo(x);
/*Mostrar el valor ingresado y lo devuelto por la funcion usando los especificadores correctos. */
printf("\n%d\t%d", x, f);
return 0;
}
/* Declaracion de funcion recursiva fibo:
* Parametro:
* - n (Cada llamada tiene su propia copia)
* Devuelve:
* - a, valor calculado de la sucesion */
int fibo(int n)
{
/* Declaracion de variable sin inicializar */
int a;
/* Caso base, si ne es menor a 2, a devuelve 1*/
if (n < 2)
a = 1;
else
a = fibo(n - 2) + fibo(n - 1); // Caso recursivo. se llama dos veces a la funcion, cada llamada genera dos nuevas llamadas. PROBLEMA: Explosion exponencial de llamadas (overflow)
return a;
}
Recursividad 05
Makefile
CC = gcc
CFLAGS = -std=c11 -Wall -Wextra -pedantic
PROG = main
OBJ = main.o
$(PROG): $(OBJ)
$(CC) $(OBJ) -o $(PROG)
$(OBJ): main.c
$(CC) $(CFLAGS) -c main.c
.PHONY: clean
clean:
del $(OBJ) $(PROG).exe
Código:
// gcc -std=c11 -Wall -Wextra -pedantic main.c -o main
/*
Comparacion recursion vs iteracion
*/
#include <stdio.h>
#include <conio.h> // No se usa nada de la biblioteca, deberia eliminarse.
/* Prototipo de funciones recursivas */
int fibo(int n); // Iterativa
int Fibo(int n); // Recursiva
int main(void)
{
/* Declaracion de variables enteras sin inicializar */
int n, fi, fr;
/* Lectura del valor de n sin validar */
scanf("%d", &n);
/* Primer llamada a ambas funciones, parametro por valor, lo que devuelve se almacena en variables enteras */
fi = fibo(n);
fr = Fibo(n);
/* Mostrar los valores que devolvieron las funciones */
printf("\n\n%d\t\t%d\n", fi, fr);
return 0;
}
/* Declaracion de funcion fibo, iterativa */
int fibo(int n)
{
/* Declaracion de variables enteras.
* fn1 y fn2 almacenan los dos terminos anteriores.
* i es contador del bucle. */
int fn1, fn2, f, i;
fn1 = 1;
fn2 = 2; // Inconsistente con la definicion recursiva
/* Caso base: si n es 0 o 1 devuelve 1. */
if ((n == 1) || (n == 0))
return 1;
/* Bucle iterativo: Calcula la sucesion sin usar recursion */
for (i = 2; i <= n; i++)
{
f = fn1 + fn2;
fn2 = fn1;
fn1 = f;
}
return f;
}
/* Declaracion de funcion recursiva Fibo */
/* Problema:
* Si n es negativo:
* - No entra en ningun if.
* - No retorna nada.
* - Comportamiento indefinido (UB). */
int Fibo(int n)
{
/* Caso recursivo:
* Si n es mayor a 1, llama dos veces.
* Complejidad exponencial. */
if (n > 1)
return Fibo(n - 1) + Fibo(n - 2);
if ((n == 1) || (n == 0)) // Caso base si n es 0 o 1 devuelve 1
return 1;
}
Recursividad 05 Extra: Modularización
Makefile
CC = gcc
CFLAGS = -std=c11 -Wall -Wextra -pedantic
PROG = main
OBJ = main.o fibo.o
$(PROG): $(OBJ)
$(CC) $(OBJ) -o $(PROG)
main.o: main.c fibo.h
$(CC) $(CFLAGS) -c main.c
fibo.o: fibo.c fibo.h
$(CC) $(CFLAGS) -c fibo.c
.PHONY: clean
clean:
del $(OBJ) $(PROG).exe
Código: fibo.c
#include "fibo.h"
/* Declaracion de funcion fibo, iterativa */
int fibo(int n)
{
/* Declaracion de variables enteras.
* fn1 y fn2 almacenan los dos terminos anteriores.
* i es contador del bucle. */
int fn1, fn2, f, i;
fn1 = 1;
fn2 = 2; // Inconsistente con la definicion recursiva
/* Caso base: si n es 0 o 1 devuelve 1. */
if ((n == 1) || (n == 0))
return 1;
/* Bucle iterativo: Calcula la sucesion sin usar recursion */
for (i = 2; i <= n; i++)
{
f = fn1 + fn2;
fn2 = fn1;
fn1 = f;
}
return f;
}
/* Declaracion de funcion recursiva Fibo */
/* Problema:
* Si n es negativo:
* - No entra en ningun if.
* - No retorna nada.
* - Comportamiento indefinido (UB). */
int Fibo(int n)
{
/* Caso recursivo:
* Si n es mayor a 1, llama dos veces.
* Complejidad exponencial. */
if (n > 1)
return Fibo(n - 1) + Fibo(n - 2);
if ((n == 1) || (n == 0)) // Caso base si n es 0 o 1 devuelve 1
return 1;
/* Arreglo para que el compilador no se queje:
* warning: control reaches end of non-valid function [-Wreturn-type]
*/
return 0;
}
Código: fibo.h
#ifndef FIBO_H
#define FIBO_H
int fibo(int n); // Iterativa
int Fibo(int n); // Recursiva
#endif
Código: main.c
//gcc -std=c11 -Wall -Wextra -pedantic main.c fibo.c -o main
/*
Extra:
Forma profesional de organizar un proyecto en C
- fibo.h => Declara las funciones (interfaz)
- fibo.c => Define las funciones (implementacion)
- main.c => Prgrama principal que utiliza la interfaz
- Makefile => Compilacion y enlazado del proyecto.
*/
#include <stdio.h>
#include "fibo.h"
int main(void)
{
/* Declaracion de variables enteras sin inicializar */
int n, fi, fr;
/* Lectura del valor de n sin validar */
scanf("%d", &n);
/* Primer llamada a ambas funciones, parametro por valor, lo que devuelve se almacena en variables enteras */
fi = fibo(n);
fr = Fibo(n);
/* Mostrar los valores que devolvieron las funciones */
printf("\n\n%d\t\t%d\n", fi, fr);
return 0;
}
Recursividad 06
Makefile
CC = gcc
CFLAGS = -std=c11 -Wall -Wextra -pedantic
PROG = main
OBJ = main.o
$(PROG): $(OBJ)
$(CC) $(OBJ) -o $(PROG)
$(OBJ): main.c
$(CC) $(CFLAGS) -c main.c
.PHONY: clean
clean:
del $(OBJ) $(PROG).exe
Código:
// gcc -std=c11 -Wall -Wextra -pedantic main.c -o main
/*
Busqueda binaria recursiva
*/
#include <stdio.h>
#include <stdlib.h> // Se usa system(), No estaba en el programa de catedra, pero sin ella no compila.
/* Falta el prototipo de la funcion declarada, se agrega para compilacion: */
int buscar(int vector[], int inf, int sup, int X);
int main(void)
{
/* Declaracion de variables y vector.
* La busqueda binaria SOLO funciona si el vector esta ordenado */
int vec[] = {2, 4, 5, 7, 8, 12, 14, 17, 21, 23, 24, 32, 43, 54, 65, 88};
int X;
int pos, longitud;
system("CLS"); // Limpia la pantalla solo en windows. No es portable.
printf("Ingrese el valor a buscar: ");
scanf("%d", &X);
/* Error: tipo no existe.
* Debe ser sizeof(vec[0]) */
longitud = sizeof(vec) / sizeof(tipo);
/* Para compilar es necesario cambiar por esta linea, descomentar tras ver el error y comentar la errada */
//longitud = sizeof(vec) / sizeof(vec[0]);
/* Almacenamos en la variable entera lo devuelto por la llamada a la funcion,
* Se pasan como argunmentos el vector ordenado, el indice inferior, el indice superior que se da de restar 1 a la longitud y el valor buscado */
pos = buscar(vec, 0, longitud - 1, X);
/* Si se encontro o no, si se encuentra se indica su posicion */
if (pos < 0)
printf("\n\nNo se encontro");
else
printf("\n\nLa posicion del valor buscado es %d", pos);
return 0;
}
/* Declaracion de la funcion buscar (Busqueda binaria recursiva).
* Se agrega el tipo int, ya que en el programa de la catedra no figuraba.*/
//buscar(int vector[], int inf, int sup, int X)
/* Busqueda binaria recursiva:
* Parametros:
* - vector => vector ordenado.
* - inf => indice inferior.
* - sup => indice superior.
* - X => valor a buscar.
*
* Devuelve:
* - indice donde esta X o -1 si no se encuentra */
int buscar(int vector[], int inf, int sup, int X)
{
/* Declaracion de variables sin inicializar */
int mitad;
int posicion; // No es utilizada
/* Caso base:
* Si el intervalo se colapso (inf > sup) o queda un solo elemento
* significa que no se encontro el elemento*/
if ((inf >= sup) && (vector[inf] != X))
return -1;
/* Se calcula el indice centtral.
* Posible problema teorico:
* - (inf + sup) podria desbordar (overflow) si los indices fueran muy grandes.
* - Fomra mas segura: inf + (sup - inf) / 2
* En este caso no es relevante por la dimension minuscula del vector.
* Se almacena el promedio en la variable entera mitad, el promedio sera truncado */
mitad = (inf + sup) / 2;
/* Si el elemento central coincide, se devuelve su posicion */
if (vector[mitad] == X)
return mitad;
/* Si el valor buscado es mayor que el central, se busca en la mitad derecha */
if (X > vector[mitad])
return buscar(vector, mitad + 1, sup, X);
/* Si el valor buscado es menor que el central, se busca en la mitad izquierda. */
return buscar(vector, inf, mitad - 1, X);
}