 So, welcome to this video session. In this video session, we're going to look at the Python implementation of coin exchange problem. And when we are considering the coin exchange problem, we will consider this problem under the canonical coin system. So, let us see what outcome has been planned for this session. At the end of this session, it is expected the student will be able to provide Python implementation for coin exchange problem under the canonical coin system. So, what is canonical coin system? The canonical coin system is the denominations what our Indian currency system uses as of now. For example, we have 1 rupee coin, 2 rupee coin, and then we have 5 rupee note, and then 10 rupee note. So, we will look at what is exactly the coin system. And there are few links provided in the description. It is highly recommended that you look at that description and understand what exactly we mean by canonical coin system. We will be only focusing on solving coin exchange problem for that canonical coin system. Fine. So, since we will be using PyCharmID and we will be using an approach called a live hands-on coding, I expect the audience has fundamental knowledge of Python language. And at the same time, they have a latest stable working copy of PyCharmID. And community edition is enough for our needs for this video. Before we go for any hands-on on implementing algorithms in Python, it is very crucial to know the time complexity of containers and collections in Python. Because when we implement a pseudocode algorithms, what we do is we write a pseudocode for algorithms and we calculate the time complexity of algorithms based on the pseudocode which we write or in general the algorithmic representation. But often when we implement those algorithms in languages, for example, we are using Python, but in some cases it could be C, Java, or even other language. So most developers what they do is they just assume or rather than saying that they will just presume that a specific operation in Python, for example, maybe it's a sorting or maybe for searching or maybe finding a mean element in a list or a specific container in a Python, they assume that it's of specific complexity. And most often this always results into a bad program implementation. So it's very important that we understand what are those operations and what is the time complexity of those operations implemented in Python's C Python runtime. So we will go through the standard time complexity manual of Python, wherein it lists you the time complexity of various crude operations on containers and collections from the Python's standard library and then the collection module as well. Apart from that, we'll also look into a module in a Python called as heap queue module, which lets me build min heaps and also which also lets me have a priority queue implementation in Python. So we'll also touch that aspect as well and we'll see what exactly will be the complexity of any heap or a priority queue constructed using those models in the Python. So let me switch to these Python's time complexity manual. So now we are on a page where it's a standard Python docs module where the Python implementation it's a Python's standard module here, where you can see here that these complexities are based on the current implementation of C Python. And this is for Python 3 we are looking at. So now when you look at this, it's what I was talking about as a more important aspect which you need to concentrate when you give a Python implement to this algorithm is these complexities. For example, if you take a list and to the list if you add an element append at the end of that list, then the complexity of that operation is over fun. So under the average case. So this is this matters so much for a theoretical consideration. But when you're writing an algorithm for a practical consideration, it's very important to also understand the immortalized worst case complexity. But as of now, we will slightly skip this, we will more talk about these complexities. So the append is off one. And you can see that sometimes when I insert this append at the beginning, then suddenly the complexity is very high. So you can see here pop last, it means that removing last element from the list is often but popping any of the middle element in the list popping any of the middle element in the list, it's complexity is often. So you can see that in our algorithm implementation, if we do this operation, then we have to consider that operation as off and not as off one. That's most misconception with developers or the noise beginners who begin with the language, they keep into mind that okay, their algorithm and the program has equal complexity. No, it depends upon how well you correlate the Python's or any language's internal implementations with your algorithms derived complexity. So since our implementation Python implementation of algorithm, which we're going to see the hands on it depends on Python, it's very important that we look at this chart. So this is for the list. And you can see here if you want a double ended queue, then this is the best data structure which limits lets you append left append right, which lets you insertion deletion at the both the end of the container and that's a off one. That's a DQ. So that's this is what is recommended. And one more operation which we very frequently we use is a sort operation and searching in a list. So when you search in a list, for example, X in a S, then it's also often finding minimum and maximum of a list is also and but you can see that surprisingly getting the length of list is off one quite contrary to the major developer perception that finding the length of a list will be an operation. It's a it's a beauty of Python, which does give you the length of list in off one. So this is set. So you can see that whenever you take a set symmetry difference or when you say whether the element is present in a set or not, so off one. So most most often we look, we need such operations in algorithm implementation. And these are the dictionaries as you can see that average case dictionaries is always off one whenever you look get item, set item and delete item. It's important that we much worry about this. But you can see that amortize worst case complex is off and okay. So fine, we'll not give much attention to this, but we'll only rely on this assumption. So iterating dictionary is n and copying the dictionary is off and don't think that creating just a copy of dictionary is one operation that no, whenever if you create any copy of a dictionary, then it is off and so by looking at this, I think the one of the another very important thing is sort, which we very frequently we use in our algorithm as you can see for sorting the complexities off and log n and it's it's somewhat based on team sort implementation. So let us switch to PyCharmID and start coding the problem. Now we are in PyCharmID. So when we talk about PyCharmID, it's a Python supporting ID where which comes with a lot of interactive tools to debug and make sure we can execute breakpoint execution with features like step into step over. So here I already wrote the code here. So I'll quickly explain what code it is and what we are executing. We are looking at the code for coin change problem. Specifically, we are that too concentrating on canonical coin system. So for example, when you see the denominations which are available here, these are called as canonical currency systems wherein I have a currency for one, one rupee, two rupee, five, 10, 20, 50 note and 105. So in order to know what is the canonical currency system, I advise you to check the links in this description of this video so that it would be far more clear to you. If it is unclear, pause the video at this point and check the links given in the description and then you can resume from here. So what we want is a given value 203, it's a value for which I need to find out minimum number of denominations with the help of which I can provide a change for this value. So in order to do that, I store the denominations here. And once I store the denominations, I calculate total number of denominations available. And here in this list, I'm going to store the denominations which I can use in order to provide a change for 203 value here. Now what I do is initially I set i is equal to number of denominations minus 1 just because I want to iterate the loop for appropriate number of iterations. And as long as i is greater than 0, what I check the value for which, so the same value, the value which I'm going to pass to this, the value as long as the value is greater than the denominations which I am traversing, if it is greater than I can easily substitute the denomination currency note from this value. So I subtract this denomination value from this, the input variable value. And since I can now use the denominations to reduce some value in this, what I do is I now add this to change coins. So this is one of the denominations which I can use to give a change for this value. And I keep on repeating till I get, till I traverse entire denominations here. And once I am done that, I am returning the change underscore coins and the same value is being printed as an output for this program. As you can see here, I have passed the input value and you can see when I pass input value here, it should print me the denominations which are available for me. So let us execute this program. So let me save this and if I want to execute, I'll just click on this and I make sure the same program is run. So now you can see the output here. The minimum number of change for 203 value from the available currency denominations is that first is 100, then the second note, it means 200 notes, 1, 2 rupee coin and 1 rupee coin. So if you look at this approach, every time when I am iterating here, I am trying to include a denomination note which can capture maximum value from the input value. So I'm looking for a gridness here wherein if I can accommodate largest value from denominations notes in the input value, then obviously I can reduce the number of notes or number of coins, I can give it to the end user. So you can guess what approach this program could be using. So this falls under the specific design strategy, algorithm design strategy as well. So this is the program for canonical coin systems, coin change problem. So now that we have seen the Python implementation for the problem, I would like to have a quick question as a reflection. What is the design approach we have used in our algorithm? So at this point you can pause the video and you can try to guess the answer. But the right answer for this is we have used a greedy approach. So that's it for this video, thank you.