Recursion in Python

Recursive functions are functions that call themselves. Recursion is often used in ways similar to iteration, and many calculations can often be computed using either process. However, iteration and recursion have their own pros and cons that can vary depending on the calculation being computed.

Recursion vs Iteration

Let's talk about factorial, the factorial of a non-negative integer n (denoted by n! ) is the product of all positive integers less than or equal to n.

$$n!=n\times n-1\times n-2\times n-3.....\times1$$

The iterative solution uses for and while loops leading to iterative algorithms.

def factorial(n):
    prod=1 
    for i in range(1,n+1):
        prod*=i
    return prod

Iterative algorithms are efficient from computer's POV.

Recursive solution

  • Reduces the problem to a simpler/smaller version of same problem.

  • A base case is used keep reducing problem until reach a simple case that can be solved directly. A base case ****to avoid ending up in an infinite loop from calling itself too many times.

def factorial(n):
    if n==1:
        #base_case
        return 1
    else:
        #recursive_step
        return n*factorial(n-1)

So in essence, recursion systematically reduces a problem (into a smaller or simpler version of that problem) until it can be solved directly and then successively returns the calculated values to the functions that called for them (in this case, factorial reduced the problem to a series of simple multiplications) until a final solution is returned.

Recursive solution may be simpler, more intuitive and recursion may be efficient from programmer's POV.

Mathematical Induction

How to know one recursive code will work? For that we can use mathematical tool, mathematical induction.

Using mathematical induction to prove a statement indexed on integers is true for all values of n:

  • Prove it is true when n is smallest value (e.g. n = 0 or n = 1)

  • Then prove that if it is true for an arbitrary value of n, one can show that it must be true for n+1

EXAMPLE : Proof the following

$$1 + 2 + 3 + ….. + n = n(n +1)/2$$

$$ \text{Let } n=1 $$

$$LHS = 1 \text{ and } RHS = 1 (1+1)/2 = 1 \text{ , so the statement is true for n =1 }$$

$$\text{Let us consider the statement to be true for}$$

$$ n=k \text{ then } 1 + 2 + 3 + …. + k = k (k+1)/2 $$

$$\text{For } n=k+1$$

$$ (1 + 2 + 3 + … + k) + (k + 1) = k(k + 1)/2 + (k + 1)$$

$$ \text{Hence we prove statement is true for}$$

$$ n=k+1$$

Let’s see a code for sum of natural numbers using iteration and recursion.

Iteration

def snat(n):
    result = 0 
    for num in range(1,n+1):
        result=result+num
    return result

Recursion

def snat(n):
    if n==1:
        #basecase
        return 1
    else:
        #recursivecase
        return n+snat(n-1)

We can use the same logic for recursive code

We will take the smallest value as base case which will be true for statement. For recursive case we can assume that it will return true value for rest cases.

Recursion with Multiple Base Cases

The Fibonacci numbers are the numbers in the following integer sequence 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ……..

In mathematical terms, the sequence Fn of Fibonacci numbers is defined by the recurrence relation

$$F_{n} = F_{n-1} + F_{n-2}$$

with seed values

$$F_{0} = 0 \hspace{0.1cm} and \hspace{0.1cm} F_{1}=1$$

def fib(x):
    if x==1 or x==0:
        #two base cases
        return 1
    else:
        # recursive case
        return fib(x-1)+fib(x-2)

Palindrome of Strings

  • Then we will solve problem recursively.

    • Base case will be the length of string is 0 and 1 is palindrome

    • recursive case will be first character matches last character, then is a palindrome if middle section is a palindrome.

      • for example
        isPalindrome("madam") is same as:
        "m"=="m" and isPalindrome("ada")
        def isPalindrome(s):

            def toChars(s):
               #converts string to characters
                s = s.lower()
                ans = ''
                for c in s:
                    if c in 'abcdefghijklmnopqrstuvwxyz':
                        ans = ans + c
                return ans

            def isPal(s):

                if len(s) <= 1:
                    #base case
                    return True
                else:
                    #recursive case
                    return s[0] == s[-1] and isPal(s[1:-1])

            return isPal(toChars(s))

Similarly we can check if some number is palindrome or not.

  • We would have to convert num into strings as numbers are not iterable.

  • Unlike in strings we don’t have to worry about punctuations as it is a number. We will directly check for palindrome in program.

    • Base case if length of a number is 1 then it is palindrome

    • Recursive case will be first character matches last character, then is a palindrome if middle section is a palindrome.

            isPalindrome(11011) is same as:
            "1"=="1" and isPalindrome(101)
            def isPalindrome(num):

                def isPal(s):
                    if len(s) == 1:
                        return True
                    else:
                        return s[0] == s[-1] and isPal(s[1:-1])

                s=str(num)

                return isPal(s)