Monografias.com > Computación > Programación
Descargar Imprimir Comentar Ver trabajos relacionados

La recursión




Enviado por Pablo Turmero



    La recursión es una técnica de
    programación asociada a la solución de problemas
    matemáticos, los cuales se definen de manera recurrente.
    Es decir, en términos de problemas más
    pequeños. Si Pn es un problema de tamaño n y F es
    una función, representamos una función recursiva
    con la siguiente fórmula

    Pn = F(Pn-1, Pn-2, Po) donde Pi es un
    Problema de tamaño i

    Veamos un ejemplo.

    Ejemplo 1: Factorial

    Su definición matemática seria

    N! = (N*(N-1)*(N-2)* … 1) para
    N>0

    0! = 1

    Si observamos el comportamiento de esta
    función:

    1! = 1

    2! = 2 * 1 = 2 * 1!

    3! = 3*2*1 = 3 * 2!

    N! = N*(N-1)!

    Para cada N tenemos una formula recurrente: N! =
    N*(N-1)!

    Si ahora queremos definir un algoritmo que acepta un
    entero N y retorna el valor del factorial de N (N!), podemos
    hacerlo de manera iterativa o recursiva.

    Si intentamos programar esta función de manera
    iterativa, usando el lenguaje C, con las herramientas que
    disponemos hasta ahora, obtendríamos una función
    como la siguiente:

    int Factorial (int A)

    {

    int prod = 1,i;

    for (i = n; i=0; i–)

    {

    prod * = i ;

    }

    return (prod);

    }

    Este es un algoritmo ITERATIVO porque requiere la
    repetición explicita de cierto proceso (multiplicar por el
    anterior) hasta alcanzar determinada condición.

    Si observamos la formula N! podemos notar que para cada
    N, esta depende del factorial calculado previamente, como
    habíamos notado anteriormente:

    0! = 1

    1! = 1*0!

    2! = 2*1 = 2*1!

    3! = 3*2*1 = 3*2!

    4! = 4*3*2*1 = 4*3!

    N! = N* (N-1)!

    Podríamos reescribir la definición de la
    función matemáticamente como:

    N! = N*(N-1)! para N > 0

    N! = 1 para N = 0

    Por lo cual la fórmula recurrente
    quedaría:

    Factorial(N)= Factorial(N-1) * N para N
    > 0

    Factorial(0)= 1 para N = 0

    En esta fórmula observamos que Factorial
    está definida en términos de si misma, pero en un
    problema más pequeño. Escribiendo el algoritmo
    recursivo en lenguaje C, basándonos en la fórmula
    recurrente:

    int factorial(int n)

    {

    if (n == 0)

    return(1);

    else

    return(n * factorial(n-1));

    }

    Note que la función se llama a si misma, a esto
    se le conoce como invocación recursiva. Este es el
    ingrediente principal de una función recursiva. Se supone
    que la función calculada ya ha sido escrita y se usa en la
    propia definición. Es importante puntualizar que la
    invocación recursiva debe hacerse con cuidado porque puede
    producirse un ciclo infinito de llamadas. Por lo tanto, en toda
    función recursiva debe existir una condición de
    parada. En el ejemplo, la condición de parada se alcanza
    en n=0.

    ¿Cómo sería la ejecución de
    una función recursiva?

    ¿Afecta el pasaje de
    parámetros?

    Para ilustrar esto efectuaremos la corrida de la
    función factorial para n=4, de forma recursiva. Llevaremos
    acabo este pequeño ejemplo paso por paso, para así
    llegar a entender de manera clara la forma en que se
    ejecutará una función recursiva.

    Sabemos de antemano que factorial(4) debe dar como
    resultado 12.

    Veamos lo que ocurre en la pila de invocación que
    se muestra en la siguiente tabla:

    Monografias.com

    Para cada nueva llamada de factorial. Se crea un nuevo
    registro de activación dentro de la pila de
    invocación donde el parámetro N cambia al valor
    indicado en la invocación. A si van a existir varias
    generaciones de parámetros simultáneamente, una en
    cada registro. Sólo se hace referencia a la copia mas
    reciente, es decir, a la copia de la generación
    actual.

    Note que en este caso usamos más memoria que el
    algoritmo iterativo. Lo cual, para este ejemplo, nos dice que es
    mejor el iterativo en cuanto a recursos. En cuanto a legibilidad,
    ambos son buenos.

    Ejemplo 2 : Números de
    Fibonacci

    La serie de Fibonacci está formada por los
    números 0,1,1,2,3,5,8,13,21,34……………

    Esta serie se construye obteniendo cada elemento como la
    suma de los anteriores. Es decir,

    0,1,0+1,1+1,1+2,2+3,3+5,5+8,8+13,13+21,…

    Debemos tomar dos números iniciales, que
    corresponden a las dos primeras posiciones de la
    seria.

    Asi, suponemos que

    Fib (0) = 0

    Fib (1) = 1

    Fib (2) = Fib (1) + Fib (0)

    Fib (3) = Fib (2) + Fib (1)

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

    Por lo que la definición matemática
    sería:

    Monografias.com

    Note que esta definición hace referencia a si
    misma dos veces. En este caso decimos que tenemos
    recursión tipo árbol. Cuando hay una sola
    llamada recursiva se dice que la recursión es por la
    cola
    .

    La implementación de la definición
    recursiva de los números de fibonacci en lenguaje C
    quedaría:

    int Fib (int n)

    {

    if ( n <= 1 )

    {

    return(n);

    }

    else

    return(Fib(n-2) + Fib(n-1));

    }

    En esta implementación, los casos bases de la
    recursión fib(0)=0 y fib(1)=1 son cubiertos por la
    condición de parada n = 1.

    La corrida de esta función es mucho más
    complicada porque tenemos dos invocaciones recursivas que deben
    resolverse en el segundo return. Veamos que ocurre para n=4,
    donde el resultado debe ser 3:

    Monografias.com

    El árbol de invocación para Fib (4)
    sería:

    Monografias.com

    Note que hay muchas llamadas redundantes, seria mucho
    más eficiente recordar el valor. Se podría hacer el
    algoritmo iterativo pero es antinatural, ya que la
    definición de la función es naturalmente recursiva.
    Escriba un algoritmo iterativo como ejercicio.

    En este caso en cuanto a recursos es mucho mejor el
    iterativo, pero en cuanto a legibilidad es mejor el
    recursivo.

    Hasta aquí parece que la recursión es algo
    formal que no me ayuda en la computación de muchas
    actividades. Veamos un ejemplo donde la recursión mejora
    significativamente el tiempo de corrida de un
    algoritmo.

    Ejemplo 3: Búsqueda Binaria

    Consideremos un arreglo de elementos donde los objetos
    se colocan en orden ascendente.

    -25

    -17

    -13

    -9

    -7

    -3

    0

    4

    10

    26

    39

    45

    49

    54

    63

    70

    Algunos ejemplos de la vida real con elementos ordenados
    son: un diccionario, un directorio telefónico, una lista
    de estudiantes, una lista de carros robados ordenados por placa,
    etc.

    Queremos encontrar un elemento dentro del arreglo. En la
    vida real, una palabra en el diccionario, la dirección de
    una persona, la nota de un estudiantes, un carro
    robado.

    La búsqueda es una actividad muy común en
    computación. Debería ser eficiente. Nosotros
    conocemos el algoritmo de búsqueda secuencial o lineal, es
    decir, buscar desde el primer elemento hasta encontrarlo. Este
    algoritmo es aplicable cuando los elementos están en
    desorden. En el caso de elemento ordenado, no tiene sentido
    aplicar este algoritmo. Naturalmente al buscar en un diccionario,
    no se busca desde el principio sino que se intenta buscar en una
    de las dos mitades, y asi sucesivamente hasta encontrar la
    palabra. Al algoritmo que se uso de manera natural al buscar en
    un diccionario se le conoce como algoritmo de búsqueda
    binaria y es el que explicaremos a
    continuación.

    Veamos la representación gráfica del
    proceso. Se pregunta por el elemento del medio (a), si
    este no es el buscado ahora se toma la mitad donde se encuentra
    el elemento. Es decir, preguntamos si el elemento es menor que a,
    en cuyo caso buscamos en la primera mitad. Nuevamente se obtiene
    el elemento del medio (b), en el sub arreglo considerado.
    Y asi sucesimente se aplica el algoritmo hasta encontrar el
    elemento buscado o llegar a una casilla donde podemos decidir que
    el elemento no se encuentra.

    Monografias.com

    Observe que la búsqueda binaria es un
    método recursivo. Pues se aplica el mismo proceso sobre el
    nuevo sub-arrego. Para determinar el sub-arreglo debemos saber
    cuáles son los límites inferior (linf) y superior
    (lsup) dentro del arreglo original, es decir, el índice
    donde comienza el sub-arreglo y el índice donde finaliza
    el mismo. Veamos el algoritmo:

    Monografias.com

    En este caso los cálculos no serian redundantes
    pues la recursión solo recorre una rama del
    árbol.

    Ejercicio: Realice la corrida para el arreglo dado como
    ejemplo y los valores de x=10 y x=15, observe lo que
    ocurre.

    Características de la Recursión

    La Recursión posee ciertas características
    que deben cumplirse para que la misma llegue a desarrollarse
    adecuadamente de lo contrario podría generar gran cantidad
    de problemas en el programa.

    • Debe tener una condición de
      parada.

    • Es un buen estilo de programación colocar
      la(s) invocación(es) recursiva(s) al final de la
      función.

    • En general es menos eficiente en tiempo y espacio,
      asi que debe considerar esto a lo hora de elegir un algoritmo
      recursivo.

    • En ocasiones es la solución más
      natural y lógica a un problema.

    • Compromiso entre la eficiencia de la máquina
      y la eficiencia del programador (costo del programa aumenta
      mas rápido que costo de
      computación).

    Ejemplo 4: Torres de Hanoi

    Monografias.com

    Figura 3.1 . Torres de Hanoi

    El problema consiste en mover N discos de un eje (A) a
    otro (C), cumpliendo ciertas reglas:

    • Un disco de tamaño mayor no se puede colocar
      sobre un disco más pequeño.

    • Sólo se puede mover un disco a la
      vez.

    "Pasar los 4 discos desde el eje A hasta el eje C usando
    B como auxiliar" es el problema que queremos resolver
    representado en la figura.

    Al tratar de definirlo en términos de un problema
    más pequeño, la definición recursiva
    quedaría

    Mover la torre de Hanoi de N-1 disco desde el eje A
    hasta el eje B

    "Mover el disco N (más grande) desde el eje A
    hasta el eje C"

    Mover la torre de Hanoi de N-1 disco desde el eje B
    hasta el eje C

    La solución trivial (caso base) es con N =
    1.

    "Mover el disco 1desde el eje A hasta el eje
    C."

    Note que la instrucción "Mover la torre de Hanoi
    de N-1 disco desde el eje A hasta el eje B" sería una
    invocación recursiva

    Monografias.com

    Figura 3.2. Ejercicio torres de
    Hanoi

     

     

    Autor:

    Pablo Turmero

     

    Nota al lector: es posible que esta página no contenga todos los componentes del trabajo original (pies de página, avanzadas formulas matemáticas, esquemas o tablas complejas, etc.). Recuerde que para ver el trabajo en su versión original completa, puede descargarlo desde el menú superior.

    Todos los documentos disponibles en este sitio expresan los puntos de vista de sus respectivos autores y no de Monografias.com. El objetivo de Monografias.com es poner el conocimiento a disposición de toda su comunidad. Queda bajo la responsabilidad de cada lector el eventual uso que se le de a esta información. Asimismo, es obligatoria la cita del autor del contenido y de Monografias.com como fuentes de información.

    Categorias
    Newsletter