Está en la página 1de 15

Recursividad.

Unidad II.- Recursividad.

2.1 Concepto de recursividad.

La recursividad es una técnica de programación importante. Se utiliza para


realizar una llamada a una funcion desde la misma funcion. Como ejemplo útil se
puede presentar el cálculo de números factoriales. Él factorial de 0 es, por
definición, 1. Los factoriales de números mayores se calculan mediante la
multiplicación de 1 * 2 *…, incrementando el número de 1 en 1 hasta llegar al
número para el que se está calculando el factorial.

El siguiente parrafo muestra una función, expresada con palabras, que


calcula 8-un factorial.

“Si el número es menor que cero, se rechaza. Si no es un entero, se


redondea al siguiente entero. Si el número es cero, su factorial es uno. Si el
número es mayor que cero, se multiplica por él factorial del número menor
inmediato.”

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.

La recursividad es un concepto importante en informatica. Muchos algoritmos


se pueden describir mejor en terminos de recursividad.

Supongamos que P es un procedimiento que contiene una sentencia de


Llamada a si mismo o una sentencia de Llamada a un segundo procedimiento que
puede eventualmente llamar de vuelta al procedimiento original P. Entonces P se
dice que es u procedimiento recursivo. Como el progrma no ha de continuar
ejecutandose indefinidamente, un procedimiento recursivo ha de tener las dos
siguientes propiedades:
(1) Debe existir un cierto criterio, llamado criterio base, por el que el
procedimiento no se llama asi mismo.
(2) Cada vez que el procedimiento se llame a si mismo (directa o
inderectamente), debe estar mas cerca del criterio base.

Un procedimiento recursivo con estas dos propiedades se dice que esta bien
definido.
Similarmente, una funcion se dice que esta definida recursivamente si la
definicion de la funcion se refiere a si misma. De nuevo, para que la definicion no
sea circular, debe tener las dos siguientes propiedades:

(1) Debe haber ciertos argumentos, llamados valores base, para los que la
funcion no se refiera a si misma.
(2) Cada vez que la funcion se refiera a si misma, el argumento de la funcion
debe acercarse mas al valor base.

Una funcion recursiva con estas dos propiedades se dice tambien que esta
bien definida.

Tipos.

Podemos distinguir dos tipos de recursividad:

• Directa: Cuando un subprograma se llama a si mismo una o mas veces


directamente.
• Indirecta: Cuando se definen una serie de subprogramas usándose unos a
otros.

Características.

Un algoritmo recursivo consta de una parte recursiva, otra iterativa o no


recursiva y un acondición de terminación. La parte recursiva y la condición de
terminación siempre existen. En cambio la parte no recursiva puede coincidir con
la condición de terminación. Algo muy importante a tener en cuenta cuando
usemos la recursividad es que es necesario asegurarnos que llega un momento
en que no hacemos más llamadas recursivas. Si no se cumple esta condición el
programa no parará nunca.

Ventajas e inconvenientes.
La principal ventaja es la simplicidad de comprensión y su gran potencia,
favoreciendo la resolución de problemas de manera natural, sencilla y elegante; y
facilidad para comprobar y convencerse de que la solución del problema es
correcta. El principal inconveniente es la ineficiencia tanto en tiempo como en
memoria, dado que para permitir su uso es necesario transformar el programa
recursivo en otro iterativo, que utiliza bucles y pilas para almacenar las variables.

2.2 Procedimientos recursivos.

Un procedimiento o función recursiva es aquella que se llama así misma.


Esta característica permite a un procedimiento recursivo repetirse valores
diferentes de parámetros. La recursion es una alternativa a la iteración muy
elegante en la solución de problemas, especialmente si estos tienen naturaleza
recursiva.
Normalmente, una solución recursiva es menos eficiente en términos de
tiempo de computadora que una solución iterativa debido al tiempo adicional de
llamada a procedimientos.
En muchos casos, la recursion permite especificar una solución mas simple y
natural para resolver un problema que en otro caso seria difícil. Por esta razón la
recursion (recursividad) es una herramienta muy potente para la resolución de
problemas de programación.

En el siguiente ejemplo se muetra un procedimiento recursivo.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace WindowsApplication1
{
public partial class Recursividad : Form
{
public Recursividad()
{
InitializeComponent();
}
double r;
int fin = 0;
private void button1_Click(object sender, EventArgs e)
{
fin = int.Parse(textBox4.Text.ToString());
listBox1.Items.Clear();
listBox1.Items.Add("x\ty");
evaluar();
}
//Procedimiento recusivo
public void evaluar()
{
while (fin <= int.Parse(textBox5.Text.ToString()))
{
r = int.Parse(textBox1.Text.ToString()) * (fin * fin) +
int.Parse(textBox2.Text.ToString()) * fin +
int.Parse(textBox3.Text.ToString());
listBox1.Items.Add(fin.ToString() + "\t" + r.ToString());
fin++;
evaluar();
}
}
}
}

Mecánica de recursión.

Un objeto recursivo es aquel que forma parte de si mismo. Esta idea puede
servir de ayuda para la definición de conceptos matematicos. Asi, la definición del
conjunto de los numeros naturles es aque conjunto en el que se cumplen las
siguientes caracteristicas:

• 0 es un número natural.
• El siguiente número de un número natural es otro numero natural.

Mediante una definición finita hemos representado un conjunto infinito.

El concepto de la recursividad es muy importante en programación. La


recursividad es una herramienta muy eficaz para resolver diversos tipos de
problemas existen muchos algoritmos que se describiran mejor en terminos
recursivos.

Dentro de la teoría de la recursión, se tiene que existen diferentes tipos de


recursión:

Recursión directa.

Cuando el código F tiene una sentencia que involucra a F.


Recursión indirecta o cruzada

Cuando la función F involucra una función G que invoca a la ves una función
H, y así sucesivamente, hasta que se involucra la función F. Por ejemplo el
algoritmo de Par o impar.

int par (int n)

{ if (n==0) return n+1;

return impar(n-1);

int impar(int n)

if (n==0) return 0;

return par(n-1);
}
A continuación se expone otro ejemplo de programa que utiliza recursión
indirecta, y nos dice si un número es par o impar. Cabe mencionar que existen
otros métodos mucho más sencillos para determinar la solución a este problema,
basta con determinar el resto de la división entre dos. Por ejemplo: si hacemos
par(2) devuelve 1 (cierto). Si hacemos impar (4) devuelve 0 (falso).

int par(int n);


int impar(int n);

int par(int n)
{ if (n == 0) return 1;

return impar(n-1);
}
int impar(int n)
{ if (n == 0) return 0;

return par(n-1);
}

Recursión simple.- Aquella en cuya función solo aparece una llamada


recursiva. Se puede transformar con facilidad en algoritmos iteractivos.

Recursión múltiple.- Se da cuando hay más de una llamada a sí misma


dentro del cuerpo de la función, resultando más difícil de transformar a iteractiva.
Poe ejemplo el algoritmo de Fibonacci.

int fibo(int n)
{ if (n<=1) return 1

return fibo(n-1) + fibo(n-2)


}
4.3.1.5.- Recursión anidada.- En algunos de los argumentos de la llamada
hay una nueva llamada a sí misma. Por ejemplo la función de Ackerman:

int Ack(int m, int n)


{ if (m==0) return n+1

if (n=00) return Ack(n-1, 1)

return Ack(m-1, Ack(m, n-1));


}

Un requisito para que un algoritmo recursivo sea correcto es que no genere


una secuencia infinita de llamadas sobre sí mismo. Cualquier algoritmo que
genere una secuencia de este tipo no terminará nunca. En consecuencia, la
definición recursiva debe incluir un componente base (condición de salida ) en el
que f(n) se define directamente (es decir, no recursivamente) para uno o más
valores de n. Debe existir una <> de la secuencia de llamadas recursivas.

Al estar trabajando con recursividad, la memoria de la computadora hace uso


de una pila de recursión, la cual se divide de manera lógica en 4 segmentos:
1. Segmento de código.- Parte de la memoria donde se guardan las
instrucciones del programa en código máquina.
2. Segmento de datos.- Parte de la memoria destinada a almacenar las
variables estáticas.
3. Montículo.- Parte de la memoria destinada a las variables dinámicas.
4. Pila de programa.- parte destinada a las variables locales y parámetros de
la función que está siendo ejecutada.

Funciones mutuamente recursivas.

Cuando se trabaja llamados a la ejecución de las funciones, se provocan las


siguientes operaciones:

1. Reservar espacio en la pila para los parámetros de la función y sus


variables locales.
2. Guardar en la pila la dirección de la línea de código desde donde se ha
llamado a la función.
3. Almacenar los parámetros de la función y sus valores de pila.
4. Al terminar la función, se libera la memoria asignada en la pila y se vuelve a
la instrucción actual.
Pero cuando se trabaja con funciones recursivas, se provoca además lo
siguiente:

1. La función se ejecutará normalmente hasta la llamada a sí misma. En ese


momento se crean en la pila nuevos parámetros y variables locales.
2. El nuevo ejemplar de función comienza a ejecutarse.
3. Se crean más copias hasta llegar a la primera llamada (ultima en cerrarse).

La ciencia de la computación hace tiempo que descubrió que se puede


reemplazar a un método que utiliza un bucle para realizar un cálculo con un
método que se llame repetidamente a sí mismo para realizar el mismo cálculo. El
hecho de que un método se llame repetidamente a sí mismo se conoce como
recursion.

La recursión trabaja dividiendo un problema en subproblemas. Un programa


llama a un método con uno o más parámetros que describen un problema. Si el
método detecta que los valores no representan la forma más simple del problema,
se llama a sí mismo con valores de parámetros modificados que describen un
subproblema cercano a esa forma.

Esta actividad continúa hasta que el método detecta la forma más simple del
problema, en cuyo caso el método simplemente retorna, posiblemente con un
valor, si el tipo de retorno del método no es void. La pila de llamadas a método
empieza a desbobinarse como una llamada a método anidada para ayudar a
completar una evaluación de expresión. En algún punto, la llamada el método
original se completa, y posiblemente se devuelve un valor.

Recursión infinita

La iteración y la recursión pueden producirse infinitamente. Un bucle infinito


ocurre si la prueba o test de continuación del bucle nunca se vuelve falsa. Una
recursión infinita ocurre si la etapa de recursión no reduce el problema en cada
ocasión de modo que converja sobre el caso base o condición de la salida.

En realidad, larecursión infinita significa que cada llamada recursiva produce


otra llamada recursiva y esta a su vez otra llamada recursiva, y así para siempre.
En la práctica, dicha función se ejecutará hasta que la computadora agote la
memoria disponible y se produzca una terminación anormal del programa. El flujo
de control de una función recursiva requiere de tres condiciones para una
terminación normal.
Cuando no utilizar recursividad.

La solucion recursiva de ciertos problemas simplifica mucho la estructura de


los programas. Como contrapartida, en la mayoria de los lenguajes de
programación las llamadas recursivas a procedimientos o funciones tienen un
coste de tiempo mucho mayor que sus homologos iterativos. Se puede, por lo
tanto, afrimar que la ejecución de un programa recursivo va a ser mas lenta y
menos eficiente que el programa iterativo que soluciona el mismo problema,
aunque, a veces, la sencilles de la estructura recursiva justifica el mayor tiempo de
ejecucion.

Los procedimientos recursivos se pueden convertir en no recursivos


mediante la introducción de una pila y asi emular las llamadas recursivas. De esta
manera se puede eliminar la recursion de aquellas partes de los programas que se
ejecutan más frecuentemente.

Ejemplo de mecánica de la recursividad

Las torres de Hanoi

Algoritmo para trasladar la torre 1…n del poste X al poste Z, usando como
auxiliar el poste Y.

Si n = 1, lleva el disco 1 de X a Z y termina.


Traslada la torre 1…n−1 usando este mismo algoritmo, de X a Y, usando
como auxiliar Z.
Lleva el disco n de X a Z.
Traslada la torre 1…n−1 usando este mismo algoritmo, de Y a Z, usando
como auxiliar X. Este algoritmo siempre me ha parecido sorprendente, parece más
la definición de un problema que su resolución. Si eres como yo tendrás que
implementarlo en un lenguaje de programación concreto para convencerte de que
funciona. En realidad, lo único que hace es especificar unos pasos inevitables. Por
ejemplo, como vimos antes, para resolver el puzzle es obligatorio llevar el disco n
de A a C, y para ello es obligatorio llevar antes la torre 1…n a B, etc. La secuencia
de movimientos que produce este algoritmo es la única solución óptima (o sea, de
longitud mínima) posible. El número de movimientos es M3(n) = 2n−1, como se
puede demostrar fácilmente por inducción sobre el número de discos.

Se cumple para n = 1
M3(1) = 1 = 21−1.
Si se cumple para n, se cumple para d+1
Al ejecutarse el algoritmo para n+1 se llama a sí mismo dos veces para n,
más un movimiento del disco n+1. Así que M3(n+1) = 2 × M3(n) + 1 = 2 × (2n−1) +
1 = 2n+1−2+1 = 2n+1−1.

Para n = 8 el número de movimientos es de 28−1 = 255. Para n = 64, de


264−1 = 18.446.744.073.709.551.615. Si los bramanes de Benarés cambiaran un
disco de sitio cada segundo necesitarían más de quinientos ochenta mil millones
de años para terminar su tarea, unas cuarenta veces la edad del Universo.

Los algoritmos recursivos funcionan bien con ordenadores, pero son difíciles
de aplicar para un ser humano. Si intentas resolver la torre de ocho discos
aplicando el método descrito es fácil que te pierdas a no ser que vayas tomando
notas de por dónde vas. Incluso para los ordenadores los algoritmos recursivos
suelen ser «poco económicos», en el sentido de que consumen bastante memoria
(y es que ellos también necesitan «tomar notas»). La alternativa a los algoritmos
recursivos son los iterativos, en los que no hay llamadas a sí mismo, sino uno o
varios bucles. Muy a menudo no existe o no se conoce una alternativa iterativa
para un algoritmo recursivo, y cuando sí se conoce, suele ser mucho más
complicada que la versión recursiva. Sin embargo, en el caso de la Torre de
Hanoi, existen varios algoritmos iterativos muy simples.

Transformación de algoritmos recursivos a iterativos.

El concepto de recursividad va ligado al de repetición. Son recursivos


aquellos algoritmos que, estando encapsulados dentro de una función, son
llamados desde ella misma una y otra vez, en contraposición a los algoritmos
iterativos, que hacen uso de ciclos while, do-while, for, etc.
Algo es recursivo si se define en términos de sí mismo (cuando para definirse
hace mención a sí mismo). Para que una definición recursiva sea válida, la
referencia a sí misma debe ser relativamente más sencilla que el caso
considerado.

Ejemplo: definición de nº natural:

→ El N º 0 es natural
→ El Nº n es natural si n-1 lo es

En un algoritmo recursivo distinguimos como mínimo 2 partes:


a). Caso trivial, base o de fin de recursión:

Es un caso donde el problema puede resolverse sin tener que hacer uso de
una nueva llamada a sí mismo. Evita la continuación indefinida de las partes
recursivas.

b). Parte puramente recursiva:

Relaciona el resultado del algoritmo con resultados de casos más simples.


Se hacen nuevas llamadas a la función, pero están más próximas al caso base.

Codigo Recursivo

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace WindowsApplication1
{
public partial class Form1 : Form
{
class cap_recursivo
{

public float capital(float m, int n, float x)


{
if (n == 0)
{ return m; }
else
{ return capital(m, n - 1, x) * (1 + (x/100)); }

public Form1()
{
InitializeComponent();

private void Form1_Load(object sender, EventArgs e)


{

private void button1_Click(object sender, EventArgs e)

{
cap sayo = new cap();

label4.Text = int.Parse(sayo.capital());

private void label4_Click(object sender, EventArgs e)


{

}
}
}

Diseño

Codigo Iterativo

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace WindowsApplication7
{
public partial class Form1 : Form
{
class cap
{
public float capital(float m, int n, float x)
{
if (n == 0)
{ return m; }
else
{
//x = x / 100;float inn;
//x = x * m;
x = x / 100;

for (int b = n; b > 0; b--)


{
m = m + (x * m);
//m = x + m;
//n = n - 1;(m ) +
}
} return m;

//return capital(m, n - 1, x) * (1 + (x / 100)); }

}
}
public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)


{

private void button1_Click(object sender, EventArgs e)


{
cap sayo = new cap();

label4.Text = int.Parse(sayo.capital());

private void label4_Click(object sender, EventArgs e)


{

}
}
}
Diseño Explicacion

Este programa lo que hace es calcular el interes de un monto inicial de


acuerdo a cuanto se pidio prestado, a cuanto porcentaje de interes y el tiempo por
el que fue prestado. Al pasar el primer año, se calcula el monto inicial + interes =
monto total, y al segundo año o mas se calcula el monto total + interes y asi
sucesivamente hasta terminar el periodo del prestamo. La forma recursiva se
vuelve a llamar, osea que vuelve a hacer el procedimiento dentro de si misma, y la
iteraria es de forma seguida y larga. No importa si va primero o despues, los dos
tipos llegan al mismo objetivo, son dos diferentes metodos de hacer la misma
accion. El recursivo manda a llamar la misma funcion dentro de si misma y
iterativo hace todo el trabajo seguido.

Recursividad en diseño.

Un procedimiento recursivo es aquel que se llama a sí mismo, para poder


funcionar tiene que tener una condición de salida que del número de veces que se
va a llamar a sí mismo el procedimiento.
La recursividad en diseño se refiere a la manera de cómo representar los
procedimientos recursivos al momento de diseñar los programas.
Dependiendo del método que utilicemos para diseñar la representación
gráfica de la recursividad va a ser diferente sin embargo el procedimiento va a ser
similar ya que el fin es el mismo poner una condición que nos diga si llamamos al
método o que nos mande terminarlo. Un ejemplo en una función genérica seria
este:
METODO1
{
Procedimiento x;
Condición ( )
{
Llamada al metodo1;
}
Fin;
}

En un diagrama seria algo así:

NOTA: Estos son dos ejemplos en ambos utilizamos una condición, pero se
pueden utilizar ciclos siempre asegurándonos de que nos den salida en algún
momento.
Lo importante y que tenemos que tomar en cuenta es que la si se cumple el
criterio para llamar el procedimiento hay que apuntar a el inicio del método, y al no
cumplirse debe apuntar al siguiente paso en el procedimiento o al final o
viceversa.

Ejemplo:

Diseñar un programa que despliegue el contenido de un arreglo el siguiente


arreglo: {a, e, i, o, u}

Seudocódigo:
1. Creamos la forma que contendrá una listbox para despliegue.
2. Creamos el arreglo que contendrá los elementos (vocales)
3. Después creamos el método despliegue. El cual contendrá:

a. Un ciclo for (int i=0;i 4;i++) dentro del cual se hará el despliegue en el
índice i del arreglo vocales
b. llamará al método despliegue.
4. En el constructor mandamos llamar al método despliegue.

Y listo al abrir nuestro programa vamos a desplegar dentro del listbox el


arreglo mediante la recursividad. Para ver un programa visual de recursividad ver
el tema recursividad.

Complejidad en los algoritmos recursivos.

Un algoritmo recursivo es un algoritmo que se define en términos de sí


mismo. Son implementados en forma de subprogramas (funciones, subrutinas,
procedimientos, etc) de tal forma que dentro de un subprograma recursivo hay una
o más llamadas a él mismo.

FUNCIÓN Factorial(n)
INICIO
SI (n<2) ENTONCES
Factorial = 1;
SINO
Factorial = n * Factorial(n-1);
FIN-SI
FIN

Generalmente, si la primera llamada al subprograma se plantea sobre un


problema de tamaño u orden N, cada nueva ejecución recurrente del mismo se
planteará sobre problemas, de igual naturaleza que el original, pero de un tamaño
menor que N. De esta forma, al ir reduciendo progresivamente la complejidad del
problema a resolver, llegará un momento en que su resolución sea más o menos
trivial (o, al menos, suficientemente manejable como para resolverlo de forma no
recursiva). En esa situación diremos que estamos ante un caso base de la
recursividad.