5. Recursión

5.1. factorial

La función factorial es muy usada en probabilidad y estadística. El factorial de n se expresa como n! y esta definido para enteros no negativos como:

$$n! = n \cdot (n - 1) \cdot (n - 2) \cdots 3 \cdot 2 \cdot 1$$

Por ejemplo \(6! = 6 \cdot 5 \cdot 4 \cdot 3 \cdot 2 \cdot 1 = 720\)

De manera Matemática el factorial se define como:

$$ n! = \Biggl \lbrace{ 1, \text{ si } n = 0 \atop n \cdot (n -1)!, \text{ si } n > 0}$$

La definición es recursiva dado que la función ! es usada en la definición de !.

def factorial(n):
    """
    Computes n * (n-1) * (n-2) * (n-3) * ... * 3 * 2 * 1
    :param n: nonnnegative integer
    :return: n!
    """
    if n == 0:
        return 1
    else:
        return n * factorial(n-1)


if __name__ == '__main__':
    print('0! = {}'.format(factorial(0)))
    print('4! = {}'.format(factorial(4)))
    print('6! = {}'.format(factorial(6)))
    print('10! = {}'.format(factorial(10)))

Una función recursiva correcta se basa en cuatro cosas:

  • La función se debe llamar a sí misma en su definición. Caso recursivo.
  • La función no debe llamarse a sí misma en su definición. Caso base.
  • Una condición selecciona entre el caso recursivo y el caso base, basándose en al menos uno de los parámetros de la función.
  • La ejecución de la función recursiva debe converger al caso base.

Como se calcularía el factorial de manera no recursiva?:

Resultado

def factorial_iterativo(n):
    """ Computes n * (n-1) * (n-2) * (n-3) * ... * 3 * 2 * 1
    :param n: nonnegative integer
    :return: n!
    """
    product = 1
    for i in range(n, 0, -1):
        product *= i
    return product

5.2. Sucesión de fibonacci

La sucesión de fibonacci es una secuencia de enteros que comienza con 1, 1 y los elementos subsecuentes son la suma de los dos anteriores.

$$ 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144 \cdots$$

Un problema común es encontrar el n-ésimo termino de la sucesión.

def fibonacci(n):
    """
    Return the nth Fibonacci number
    :param n: index in the Fibonacci sequence
    :return: the nth Fibonacci number
    """
    if n <= 2:
        return 1
    else:
        return fibonacci(n - 1) + fibonacci(n - 2)


if __name__ == '__main__':
    print('f(1) = {}'.format(fibonacci(1)))
    print('f(5) = {}'.format(fibonacci(5)))
    print([fibonacci(i) for i in range(1, 20)])

5.3. Suma recursiva

Como se puede observar la recursion sirve para dividir el problema en problemas más pequeños y asi obtener la solución al problema original.

Define una función recursiva que sume los primeros n números naturales:

$$ n + (n-1) + … + 1 $$

Resultado

def sum_to_n(n):
    """
    sum the first n natural numbers
    :param n: index
    :return: n + (n-1) + ... + 1
    """
    if n == 1:
        return 1
    return n + sum_to_n(n-1)


if __name__ == '__main__':
    print('5 + 4 + 3 + 2 + 1 = {}'.format(sum_to_n(5)))

Una definición recursiva de la suma de dos números es la siguiente:

$$ suma(m, n) = \Biggl \lbrace{ m, \text{ si } n = 0 \atop 1 + suma(m, n - 1), \text{ si } n > 0}$$

Resultado

def suma(m, n):
    """
    add two numbers recursively
    :param m: integer
    :param n: integer
    :return: m + n
    """
    if n == 0:
        return m
    return 1 + suma(m, n - 1)