Recursividad en C++
Recursividad en C++:
La recursividad es una técnica de programación elemental que permite que una
función pueda llamarse asimismo desde la misma función. Se puede utilizar la
recursividad como una alternativa a la iteración. La recursividad es una herramienta
poderosa e importante en la resolución de problemas en programación. Una solución
recursiva es normalmente menos eficiente en términos de tiempo de computadora que
una solución iterativa debido a las operaciones auxiliares que llevan consigo las llamadas
suplementarias a las funciones: sin embargo, en muchas circunstancias el uso de la
recursión permite a los programadores especificar las soluciones naturales, más lógicas,
elegantes, sencillas, que serían, en caso contrario difícil de resolver.
No todas las funciones pueden llamarse a sí mismas, sino que deben estar
diseñadas especialmente para que sean recursivas, de otro modo podrían conducir a bucles
infinitos, o a que el programa termine inadecuadamente. Tampoco todos los lenguajes de
programación permiten usar recursividad. C++ permite la recursividad.
Cada vez que se llama a una función, se crea un juego de variables locales, de este
modo, si la función hace una llamada a sí misma, se guardan sus variables y parámetros,
usando la pila, y la nueva instancia de la función trabajará con su propia copia de las
variables locales. Cuando esta segunda instancia de la función retorna, recupera las
variables y los parámetros de la pila y continúa la ejecución en el punto en que había sido
llamada.
Por ejemplo para calcular el factorial de cualquier número mayor que cero hay
que calcular como mínimo el factorial de otro número. La función que se utiliza es la
función en la que se encuentra en estos momentos, esta función debe llamarse a sí misma
para el número menor inmediato, para poder ejecutarse en el número actual. Esto es un
ejemplo de recursividad.
De esta forma podríamos crear una función recursiva para calcular el factorial de
un número entero.
El factorial se simboliza como n!, se lee como "n factorial", y la definición es:
n! = n * (n-1) * (n-2) * ... * 1
Hay algunas limitaciones:
No es posible calcular el factorial de números negativos, no está definido.
El factorial de cero es 1.
De modo que una función bien hecha para cálculo de factoriales debería incluir
un control para esos casos:
/* Función recursiva para cálculo de factoriales */
int factorial(int n) {
if(n < 0) return 0;
else
if(n > 1) return n*factorial(n-1); /* Recursividad */
return 1; /* Condición de terminación, n == 1 */
}
Veamos paso a paso, lo que pasa cuando se ejecuta esta función, por ejemplo:
factorial (4):
1aInstancia
n=4
n>1
salida ← 4 * factorial (3) (Guarda el valor de n = 4)
2aInstancia
n>1
salida ← 3*factorial (2) (Guarda el valor de n = 3)
3aInstancia
n>1
salida ← 2*factorial (1) (Guarda el valor de n = 2)
4aInstancia
n == 1 → retorna 1
3aInstancia
(recupera n=2 de la pila) retorna 1*2=2
2aInstancia
(recupera n=3 de la pila) retorna 2*3=6
1aInstancia
(recuperan=4delapila)retorna6*4=24
Valor de retorno → 24
Aunque la función factorial es un buen ejemplo para demostrar cómo funciona
una función recursiva, la recursividad no es un buen modo de resolver esta función, que
sería más sencilla y rápida con un simple bucle for.
Una función recursiva tiene generalmente dos partes:
1) Una llamada recursiva con parámetros más sencillos.
2) Una parte de terminación que detiene la recursividad.
Principios de la recursividad:
La recursividad es una de las formas de control más importantes en la
programación. Los procedimientos recursivos son la forma más natural de representación
de muchos algoritmos.
El concepto de recursividad está ligado, en los lenguajes de programación, al
concepto de procedimiento o función. Un procedimiento o función es recursivo cuando
durante una invocación a él puede ser invocado a su vez él mismo.
El conocimiento de los principios fundamentales de la recursividad evita evadir
su utilización cuando su aplicación sea conveniente para un determinado problema.
El uso de la recursividad es particularmente conveniente para aquellos problemas
que pueden definirse de modo natural en términos de recursividad.
Ventajas y desventajas de la Recursividad:
Ventajas:
No es necesario definir la secuencia de pasos exacta para resolver el problema.
Soluciones simples, claras.
Soluciones elegantes.
Soluciones a problemas complejos.
Desventajas:
Podría ser menos eficiente.
Sobrecarga asociada con las llamadas a subalgoritmos
Una simple llamada puede generar un gran número de llamadas Recursivas. (Fact(n)
genera n llamadas recursivas)
El valor de la recursividad reside en el hecho de que se puede usar para resolver
problemas sin fácil solución iterativa.
La ineficiencia inherente de algunos algoritmos recursivos.
Ejemplos de funciones recursivas en C++:
1. // Sumar los dígitos de un número
#include <iostream>
using namespace std;
int SumaDigitos(int);
int main() {
cout << 32890123 << ": " << SumaDigitos(32890123) << endl;
return 0;
}
int SumaDigitos(int n) {
if(n < 10) return n;
return n%10+SumaDigitos(n/10);
}
2. // El producto de forma recursiva
#include <iostream>
using namespace std;
int producto(int a, int b){
if (b==0)
return 0;
else
return a+producto(a,b-1);
}
3. // Suma recursiva de los elementos de un vector
#include <iostream>
using namespace std;
int SumaV (int *V, int n){
if (n==0)
return V[0];
else
return V[n]+sumaV(V,n-1);
}
4. // Buscar el máximo de un vector (I)
#include <iostream>
using namespace std;
int Mayor1 (int *V, int n){
int aux;
if (n==0)
return V[0];
else {
aux = Mayor1 (V, n-1);
if (V[n]> aux)
return V[n];
else
return aux;
}
5. // Búsqueda lineal recursiva (con dos casos base)
#include <iostream>
using namespace std;
int BusquedaLineal(int *V, int n, int b)
{
if (n<0)
return 0;
else
if (V[n]==b)
return 1;
else
return BusquedaLineal(V,n-1,b);
}
6. // Función de partición
#include <iostream>
using namespace std;
int partir (int *v, int primero, int ultimo);
void OR (int *v, int izda, int dcha)
{
int pos_pivote; // Pos. del pivote tras partición
if (izda < dcha) {
// Particionar "v"
pos_pivote = partir (v, izda, dcha);
// Ordenar la primera mitad
OR (v, izda, pos_pivote-1);
// Ordenar la segunda mitad
OR (v, pos_pivote+1, dcha);
}
}
int partir (int *v, int primero, int ultimo)
{
void intercambia_int (int &a, int &b);
int izda, dcha; // Indices para recorrer v
int val_pivote; // Valor del pivote.
// El pivote es el primer elemento.
val_pivote = v[primero];
izda = primero + 1; // "izda" va a la dcha.
dcha = ultimo; // "dcha" va a la izda.
do { // Buscar e intercambiar elementos
// Buscar un elemento mayor que el pivote
// avanzando desde la izquierda
while ((izda<=dcha) && (v[izda]<=val_pivote))
izda++;
// Buscar un elemento menor o igual que el pivote
// avanzando desde la derecha
while ((izda<=dcha) && (v[dcha]>val_pivote))
dcha--;
if (izda < dcha) { // Intercambiar
intercambia_int (v[izda], v[dcha]);
dcha--;
izda++;
}
} while (izda <= dcha); // Terminar cuando se
//cruzan "izda" y "dcha"
// Colocar el pivote en su sitio correcto
intercambia_int (v[primero], v[dcha]);
return dcha; // Devolver la pos. del pivote
}
7. // Torres de Hanói
#include <iostream>
using namespace std;
int main (void)
{
void hanoi (int n, int inic, int tmp, int final);
int n; // Numero de discos a mover
cout << "Numero de discos: ";
cin >> n;
hanoi (n, 1, 2, 3); // mover "n" discos del 1 al 3,
// usando el 2 como temporal.
return 0;
}
void hanoi (int n, int inic, int tmp, int final)
{
if (n > 0) {
// Mover n-1 discos de "inic" a "tmp".
// El temporal es "final".
hanoi (n-1, inic, final, tmp);
// Mover el que queda en "inic"a "final"
cout <<"Del poste "<<inic<<" al "<<final<<"\n";
// Mover n-1 discos de "tmp" a "final".
// El temporal es "inic".
hanoi (n-1, tmp, inic, final);
}
}
% Hanói
Numero de discos: 3
Del poste 1 al 3
Del poste 1 al 2
Del poste 3 al 2
Del poste 1 al 3
Del poste 2 al 1
Del poste 2 al 3
Del poste 1 al 3
8. // Visualizar las permutaciones de n elementos.
Las permutaciones de un conjunto son las diferentes maneras de colocar sus
elementos, usando todos ellos y sin repetir ninguno. Por ejemplo para A, B, C, tenemos:
ABC, ACB, BAC, BCA, CAB, CBA.
#include <iostream>
using namespace std;
/* Prototipo de función */
void Permutaciones(char *, int l=0);
int main(int argc, char *argv[]) {
char palabra[] = "ABCDE";
Permutaciones(palabra);
[Link]();
return 0;
}
void Permutaciones(char * cad, int l) {
char c; /* variable auxiliar para intercambio */
int i, j; /* variables para bucles */
int n = strlen(cad);
for(i = 0; i < n-l; i++) {
if(n-l > 2) Permutaciones(cad, l+1);
else cout << cad << ", ";
/* Intercambio de posiciones */
c = cad[l];
cad[l] = cad[l+i+1];
cad[l+i+1] = c;
if(l+i == n-1) {
for(j = l; j < n; j++) cad[j] = cad[j+1];
cad[n] = 0;
}
}
}
9. // Búsqueda binaria
#include <iostream>
using namespace std;
int Binaria(int*, int, int, int);
int tabla[] = {
1, 3, 12, 33, 42, 43, 44, 45, 54, 55,
61, 63, 72, 73, 82, 83, 84, 85, 94, 95,
101, 103, 112, 133, 142, 143, 144, 145, 154, 155,
161, 163, 172, 173, 182, 183, 184, 185, 194, 195
};
int main() {
int pos;
int valor=141;
pos = Binaria(tabla, valor, 0, sizeof(tabla)/sizeof(tabla[0])-1);
if(pos > 0) cout << "Valor " << valor << " encontrado en posicion: " << pos << endl;
else cout << "Valor " << valor << " no encontrado" << endl;
return 0;
}
/* Función de búsqueda binaria:
Busca el "valor" dentro del vector "vec" entre los márgenes
inferior "i" y superior "s" */
int Binaria(int* vec, int valor, int i, int s) {
int inferior = i;
int superior = s;
int central;
if(superior <= inferior) return -1;
central = inferior+(superior-inferior)/2;
if(vec[central] == valor) return central;
else if(vec[central] < valor) return Binaria(vec, valor, central+1, superior);
return Binaria(vec, valor, inferior, central-1);
}
10. // Algoritmo de Euclides
#include <iostream>
using namespace std;
int mcd(int, int);
int main() {
int a, b;
a = 364332;
b = 30252;
cout << "mcd(" << a << ", " << b << ")= " << mcd(a,b) << endl;
return 0;
}
int mcd(int a, int b) {
if(a < b) return mcd(b,a);
if(b == 0) return a;
return mcd(b, a % b);
}