Understanding Recursion

December 21, 2019   

To Understand Recursion, one must first understand Recursion

  -   Stephen Hawking

Introduction

Recursion is that type of programming construct that first bewilders those who take their first glance at it and then frightens only until they take a step forward fearlessly. Once taken, they’ll realise that it’s just like that dragon that’s hard to tame but nevertheless the strongest.

So, without any further ado, let’s just barge in.

Today, I have come across a video on Youtube by interviewing.io called Python interview with a Google Engineer: Coin Change

Before getting into solving the problem, let’s first try and understand the Problem itself.

Problem Statement

Suppose, You are at a grocery store and about to buy some groceries worth INR 99.15/-. You give the shopkeeper at the counter an INR 100/- bill and now the shopkeeper must return the exact change back to you and not some mints or chocolates like they got used to.

We know that, he has to return 85 paise (100 - 99.15) and (assume) we have only the following denominations of coins available.

  • 50 paise
  • 25 paise
  • 05 paise
  • 01 paise

We have to calculate the number of coins that the shopkeeper has to return to return the exact change. As per the premise above, the shopkeeper has to return:

  • 1 of 50 paise coin
  • 1 of 25 paise coin
  • 2 of 05 paise coin

=> a total of 4 coins.

Now, what we have to do is, write a program for the same, i.e.one that returns the number of coins to be returned by the shopkeeper.

Hoping that you’ve understood the problem, let’s now get into solving it.

Approach Towards Solution

All we’ve to do is to write a function that takes in the amount (input) that has to be given back to the customer and returns the number of coins to return.

For this, let’s first define a function with the input parameter and return value.

def coins_change(amount):
    # Calculation comes here
    return no_of_coins

Now, somehow, we have to store the denominations of coins available to be able to return the exact number of coins to return. There exists decent room for optimisation here, which is, considering the premise above, when the shopkeeper has to return 85 paisa, he can do it by giving 85 nos of 1 paisa coins or 19 nos of 5 paisa coins etc, instead, the shopkeeper always tries to get rid of only least nos of coins. For which, he starts picking the coins from the highest denomination possible and proceeds towards the least, which means that he picks one coin from the 50 paisa drawer, then one from the 25 paisa drawer and so on, and this is exactly what our program has to do.

For this, I felt like having a dictionary data structure with the denominations of the coins as keys and the number of coins of that denomination that are available as values.

Any data structure among lists, tuples and sets can be used for this data but just to make the program closer possible a real-time’s simulation, I have decided to go with dictionaries.

This data structure can be created within or outside the function as it doesn’t matter much for our current problem statement as the number of coins available isn’t of our concern with respect to the current problem statement

def coins_change(amount):
    denominations = {50:5, 25:5, 5:10, 1:20}
    # Calculation comes here
    return no_of_coins

Now the actual solution starts. From the number of coins available of each denomination, we have to start from the highest denomination available and possible.

=>1 if I have to return 35 paise, then I have to start picking the coins from 25 paise denomination slot and then from the 5 paise coins slot.

=> If I have to return 9 paise, then I have to pick 1 of the 5 paise coins and 4 of 1 paise coins.

=> We’ve to read the keys of the dictionary in a descending order for which I have passed it to the sorted() with the parameter for reverse keyword argument being True which returns the keys of the dictionary in the required descending order.

def coins_change(amount):
    denominations = {50:5, 25:5, 5:10, 1:20}
    for denomination in sorted(denominations, reverse = True):
    # Calculation comes here
    return no_of_coins

Now, at each iteration of the for loop, we get each denomination available in descending order.

Therefore, we’ve to first check if the amount (the change we’ve to return) is bigger than the denomination at hand (the value, the denomination object is holding in the iteration). If it turns out to be false (=> if amount < 50 paisa) , go to the next denomination, else (if amount > 50 paisa, as in our case, 85 paisa), we’ve to calculate the number of coins of 50 paise to return and this will be given by the Floor Division ( // ) operator and the remaining amount to be given is determined by the Modulo ( % ) operator.

  • Floor Division for number of coins:
>>>85 // 50
1
  • Modulo Division for remainder:
>>>85 % 50
35

And here comes the crucial part.

Now, if the amount >= denomination , then calculate the no_of_coins that are to be given and check if the remainder, amount - (no_of_coins * denomination) , is greater than 0 . If false , meaning there is no remainder, therefore, return no_of_coins but if true , meaning that remainder exists, return no_of_coins + , since we are yet to calculate the number of coins to be returned for the remaining amount, we return coins_change(amount % denomination) . Where,

no_of_coins = amount // denomination , and

remaining amount = amount - no_of_coins * denomination.

The above mentioned property of a function calling itself (as in the return statement mentioned above) is called Recursion

Here’s the code for the same:

def change_coins(amount=None):
    denominations = {50: 5, 25: 5, 5: 10, 1: 20}
    if amount:
        for denomination in sorted(denominations, reverse=True):
            if amount >= denomination:
                if amount % denomination:
                    return (amount // denomination) + change_coins(amount % denomination)
                else:
                    return amount // denomination

Now for a clearer understanding, let’s check how this function works by analysing the working of the function at each step.

Applying the Function to Validate the accomplishment

Steps

  1. change_coins(85) , _calling the function with the amount to be returned to the customer_

  2. if amount: , check if amount is not None , just to make sure that the function literally does nothing when nothing is to be returned.

  3. for denomination in sorted(denominations, reverse=True): == for denomination in [50, 25, 5, 1]:

    • in the first iteration, denomination = 50
    • if amount >= denomination: => if 85 >= 50: = True
      • if amount % denomination: => if 85 % 50: => if 35 = True because, 35 > 0 => remainder exists!
        • return (amount // denomination) + change_coins(amount % denomination) => return ( 85 // 50) + change_coins( 85 % 50) => return 1 + change_coins(35)
          • change_coins(35), _calling the function with the amount to be returned to the customer again_
          • for denomination in [50, 25, 5, 1]:
            • in the first iteration, denomination = 50
            • if amount >= denomination: => if 35 >= 50: = False
            • in the second iteration, denomination = 25
            • if amount >= denomination: => if 35 >= 25: = True
              • return ( 35 // 25 ) + change_coins( 35 % 25 ) == return 1 + change_coins(10)
                • change_coins(10), _calling the function with the amount to be returned to the customer again_
                • for denomination in [50, 25, 5, 1]:
                  • in the first iteration, denomination = 50
                  • if amount >= denomination: => if 10 >= 50: = False
                  • in the second iteration, denomination = 25
                  • if amount >= denomination: => if 10 >= 25: = False
                  • in the third iteration, denomination = 5
                  • if amount >= denomination: => if 10 >= 5: = True, but
                    • if 10 % 5: = False because, 0 is a falsy value and there it
                    • return 10 // 5 => return 2

change_coins(85) = return 1 + change_coins(35) = return 1 + change_coins(return 1 + change_coins(10)) = return 1 + (return 1 + change_coins(10)) = return 1 +(return 1 + (return 2)) = 1 + (1 + (2)) = 1 + 1 + 2 = 4


  1. a symbol meaning implies [return]


comments powered by Disqus