 Welcome back, in the last segment we discussed the merge function and earlier we had discussed the merge sort function and now we are going to put together everything and analyze the entire merge sort algorithm and then we will also do a demo and this will conclude this entire lecture sequence, so we will have some remarks at the end. So this was our merge sort function, I will just copy it over here for reference, so we are going to analyze the time it takes, so we need some notation. So let us say that T of i is the maximum time required for a merge sort to sort any sequence of length i. So the time required might be different for different sequences but I am just saying what is the maximum possible time to sort any sequence of length i, so let me call that T i. So this is an unknown and we are going to put down some conditions on this unknown and then we will be able to estimate this unknown. In fact this is not just a single unknown, I am defining several unknowns at the same time, so I am defining several unknowns for an unknown for every value of i over here. So we can say something about T1, what is T1? The time required for merge sort to sort a sequence of length 1, well that is very easy, so if we execute this at this point itself we are going to return. So it will take some time but it will be some fixed amount of time, so let us say it will take some time c. So I could write T1 equal to c over here also but since everything else is going to be less than I will write this also as less than or equal to. So now I claim that T of n has to satisfy this inequality, so let us see why. So this green part, so Tn is the time required for executing this entire function for this value of n. So this green part I claim comes from this part. So we are giving merge sort a sequence of length n, so what do we do with it? So we copy parts of it into u and parts of it into v, the total number of elements that we are copying is n. So how much time does this copying take? Well it takes time proportional to n and therefore we can write this as d times n. Then we are going to do a merge sort on n by 2, a sequence of length n by 2. So that I can estimate by one of these Tn by 2s. And this is really merge sort of n minus n by 2 but let us say n is even for simplicity. And then therefore this also takes time n by 2 at most and so this time must be 2 times T of n by 2, so for this red part. And then finally we are going to do a merge of these two small sorted sequences. How much time does that take? So in the previous segment we said that the merge itself takes time proportional to n, the proportional to the final sequence that results from merging these two sequences and that we know is n. Actually I do not need this argument over here, so we can drop this argument, we do not need it over here but yeah. So what I have said over here is that the green part is creating UV, salt halves and the merge. So I can write, I can simplify this and I can write f for d plus e and so I can say that T of n is fn plus 2 times T of n over 2. As I said earlier we have several such variables T1, T2, T3 and similarly over here, so I am really writing not just one inequality but I am writing lots of inequalities at once. So every n will satisfy something like this, but what does that mean? This means that this inequality also applies to Tn by 2 and let us say n by 2 is an integer, so it applies to Tn by 2 also. So what is this inequality for Tn by 2? Well, so just substitute n by 2 over here, so fn will get n by 2 over here and if I substitute n by 2 I get 2 times T of n by 4. But now I can substitute this into this, so what do I get? So I will get fn plus this fn by 2 to this times 2, so this is the 2 that comes from here. So what is this? So I get an fn over here, then there is this 2 and this 2 cancels, so I will get an extra fn, so that is what comes over here and then this 2 and this 2 will give me a 4Tn by 4. Now I am going to assume that this n is a power of 2, so I can sort of keep on substituting into this. So I will ask you to work it out, but if you keep on substituting into this, what we are going to get is that T of n, if I do this k times I will get a k over here and as you can see the power over here is doubling and it is the same in the denominator over here, so I will get something like 2 raise to k times T sub n over 2 to the k, so this is a little hard to read, but this is n divided by 2 raise to k over here, this 2 raise to k is in this fraction over here. Now I cannot do this at infinitum of course, it has to stop when this number, this number over here say becomes 1 and I said n is the power of 2, so when will this number become 1? Well if n is 2 to the k, so if I choose k to be such that n is 2 to the k or in other words if I choose k to be log of n to the base 2 then this will stop, but at that time what will it be? So at that time it will be k will be log n, so this will become fn times log n and this will be n times T1, but n times T1 is c, so this whole thing is less than fn log n plus c plus nc, but this n log n term is going to dominate and therefore I can write T of n has to be smaller than some constant times n log n, so which is exactly the result that we wanted, so this says that the time taken for sorting is some constant times n log n rather than some constant times n squared and n log n is really much smaller than n squared and that explains why or that suggests that more sort should be much faster. And I have made an assumption over here that n is the power of 2, but that is only a technical assumption, I mean just to simplify the algebra over here, if it is not a power of 2 all of these things actually work out but then algebra gets messier and so let us not worry about it, the main ideas are already here. So we are now going to do a demo, our program is more sort, so it is really the same code but I want to print our array at different point, so I have written a function print over here and that gets called, so again in the merge I am printing what array gets passed at the beginning and also the result that is produced at the end and merge sort also I am printing a message saying I am sorting this array over here and at the end I am printing a message saying sorted and this is the array to be sorted, this is merge sort and that is the main program. So let us compile this and run it and let us run it, so there is a certain amount of output over here, let us start at the beginning and let us see what it is doing. So the first call to merge sort was this, these, this in the array, now if you remember what this was doing it was splitting the problem into two parts and it was recursing, so indeed that is what is happening over here, this is the first half on which it was splitting and it is recursing on it but what happens next, it splits on this and it recurses on this but if you recurs on 50 there is nothing to be done and so this comes back, this returns immediately. Next the recursion on this is taken up, so that causes the smaller problems of size 1 to be created so that those also get returned immediately. So now we have two sorted sequences which were created and there, so we have a call here on the merge function, so the result of merging these two things is 2987. So this was returned to this sorting call, so there was a merge sort call with this as arguments that led to this all of this, this merge business and therefore the result of this sorting call is all of this, so you can keep going in this manner, you can look through this just to see, just to understand how this sorting is happening or better still you really should draw out that recursion tree to see, to understand how the sorting actually happens or you can just say that look I do not need to know these details, I need to know and the way I think about recursion is I just look at the top level, the top level call and the recursive calls and I assume that the recursive calls happen correctly if in that case can I be sure that the top level calls also happen correctly, so that is also fine but if you need to know, if you want to know what the details are you can certainly run this program, maybe add even more print statements to it so that you can see what is going on, alright, so that really concludes this lecture and what did we do in this lecture, so we studied binary search and we wrote the code for it and we also observed and we also analyzed as to why it is going to be faster than linear search and similarly we wrote the code for merge sort and we analyzed that it has to be much faster than the selection sort code that we wrote earlier in the course, both of these algorithms have a similar idea which is that we take our original problem, the problem that is given to us and we divide it into two parts and we in the case of binary search we only work on one of those parts, we somehow know and that is what the sorting gives us that we do not need to work on one part at all, in the case of merge sort we have to work on both parts but in any case we are looking at a big problem as being decomposed into smaller problems and we have to work on that and this idea of viewing and solving problems in this manner is often called divide and conquer, so what we have seen is our examples of divide and conquer and recursion works nicely with divide and conquer but recursion can also work quite nicely in other cases, so in any case we now have seen two examples of recursions with arrays and you can certainly note that both of these are very nice applications and very nice algorithms and very useful algorithms, so we will stop over here but I will as always urge you to look at the problems on at the end of this chapter, thank you.