 First let us do a short summary of what we have done so far after figuring out the basic data types and expressions. So, data types were like integer, float, double, long etcetera. And basic expressions were arithmetic and logical expression. In arithmetic expressions you could do you know the usual add, subtract, multiply, divide as well as invoke library functions to calculate power, square roots, other roots, log, exponential. And you could therefore build up numeric expressions. In case of logical expressions you could compare two numbers or look for other conditions that would be a Boolean value. And you could then combine the Boolean values using and or not and the usual Boolean operators. Also provided in C plus plus is this capability of doing bitwise manipulation between arrays of bits which could be bytes or integers or shorts etcetera. So, after we got around that we started looking at how to control the execution of statements depending on dynamic conditions in the code which is basically based on values that variables take. And that led us to the if then else statement and then the switch statement followed by the looping constructs while and for. As some examples of using these constructs simultaneously in writing interesting or useful programs we saw these examples. So, printing numbers to any radix in particular decimal binary or hexadecimal raising a number to a power and we saw that you can do it in a couple of ways. The squaring trick gave us a logarithmic number of multiplications in the input power whereas, if you just did it linearly one power by one power you take linear time. We looked at how to compute factorials of numbers how to sum convergent series have some kind of an epsilon. So, that we can bail out of summing more and more once you reach that threshold that would give an approximation to the true quantity. So, you can find out e or pi or many other things that were log. Then we looked at taking limits. So, and one example was calculating pi using the method of Archimedes and we saw the dangers of taking limits without control on precision. So, eventually your machine architecture gets in trouble because a limit may be of the form 0 by 0 or 0 times infinity in which case you have to take great care to stop at the right point after which the machine will not get you any more precision. In fact, the machine can make it worse. Similarly, we looked at numerical definite integration between limits. We did it for now using a very simple piecewise constant approximation of the function. The function was a smooth curve we chopped it up into vertical strips or rectangles. We assume that the upper limits of those rectangles are sharply cut off. You can do that either to be a lower bound to the function or an upper bound to the function and then we added the idea up and that gave us an approximation to the definite integral of that function. There are more fancy ways of doing it. Instead of taking a piecewise constant approximation to the function which looks like a bunch of steps at the top, you could take a piecewise linear approximation. So, you could find out say if a function is going like this and your rectangle is like that at the base. So, this is x and that is x plus h. Instead of taking either the lower step or the upper step, you could take a chord and that is called a piecewise linear approximation to the function. The area of a trapezoid is known. So, you can evaluate that in closed form. In particular, the area here would be h times the average of the two heights which is f x plus f x plus h divided by 2. That is the area of the rectangle and this is the little more computation. Instead of just taking h into f x which would be this area, you are making this correction. So, by making that correction, you would more quickly approach the area of the true curve, area under the true curve. So, that is how you would do definite numerical integration and you can go further than that. Instead of a first order approximation, you could make a second order approximation using part of a parabola. So, the higher degree polynomial you use, the better fit you will get to the original curve. That is the understanding and there are numerical methods to do that. So, there are more and more precise ways to do definite integration. Then we looked at computing discrete series recurrences. So, then we looked at computing discrete series recurrences. For example, the Fibonacci series or migration of population between two cities. Anytime, you can express a current quantity as a function of some recent quantities. That is the kind of pattern where you set up this sort of updates. We do not need to remember the whole series back to minus infinity. You can just remember the last few values and then we looked at the shifting trick by which you reuse a small set of variables to track what is happening to the series. Then we looked at greatest common divisor and how using numerical properties or number theory properties can help us write invariance. You get an original call to GCD of m comma n and we establish certain relations between what happens if you divide m by n and so on. We transform the input and we continue with that. That is what is called an example of tail recursion. So, when we come to general recursive functions, we will see other examples of that. Then we started looking at nested loops. One example of that was to compute the value of pi again by dividing the plane into a bunch of fine grid points and summing up the number of grid points inside a disk. We also looked at nested loops in connection with string manipulations. So, we learnt how to reverse a string, compare two strings, compare strings of unequal length and also find a needle string inside a haystack string. So, there are some of the natural applications of loops. We will start with the next module of the course which is, we are not leaving loops behind by any means, but to spice up things a little more, we are going to introduce a new kind of data structure which is called an add-in. So, so far we have been looking at variables which were so called scalar variables namely each int double character or short had a distinct name. So, you say someone salary that salary is one integer or one floating point number, you say temperature that is one floating point number. And array is the first example that we will see which is a collection of variables. Suppose I want to represent not the current temperature, but the last 1000 readings of temperature I have got from an AC or some room and so on from a sensor. And naturally I do not want to create a 1000 different names. I do not want to say double temperature now, double temperature 1 minute before, double temperature 2 minutes before declaring so many variables would be very cumbersome. Therefore, collection like an array uses a single name that is shared by all the scalar values inside the collection. However, to distinguish between multiple positions or multiple atomic values inside the collection, they use what are called different indices. Adders are provided in C plus plus in two different ways. One we will call the native array which is supported by the language itself. That has certain shortcomings which we will see gradually. And then there is the vector type which uses some of these issues and takes away memory management headaches and is much easier to use and safer to use. And, but they are very similar when it comes to reading elements and writing elements from an array. So, if you understand one, you will easily understand both. For simplicity, I will start with the native array situation and then near the end of today's lecture or the early next lecture, we will move into the vector class. Now, one element of warning is that I have been using this string and the vector class as black boxes. So, if you follow the lecture and then afterwards you go to the web and say, let me find out more about strings or more about vectors. Chances are you will get more confused than clarified because the web content will start explaining how vectors are implemented inside and what all methods are available for doing great and dangerous things. That is not the point here. The reason why I want to introduce vectors early is that it is a very simple black box drop in replacement for native arrays which is easier and safer to use. So, do not go digging into how vectors or strings are implemented. We will do that a lot later. When you look into how to write our own objects, then we will dig up those codes and see how they have implemented those objects. So, do not dig into those things yet. So, notation in writing standard math, we write subscripts lower than in smaller fonts. So, if a is a sequence or b is a sequence, we write a subscript i or b subscript j. Sometimes we already write a with a bracket i or b with a bracket j. For writing code, the subscript is placed within box brackets. So, we will write a sub i as a box i and b box j etcetera. Inside the box bracket, there can be an arbitrary integer expression. In C plus plus as well as C and java and many other languages, the convention is that the first element is at index 0 and if the array has n elements, then the last element is at position or index n minus 1 as in a n minus 1. Now, when we compute that integer expression which finally gives us the index, we have to be always careful in our code that the resulting value of the expression is between 0 and n minus 1 inclusive. If the value goes negative or if the value goes beyond n minus 1, depending on the implementation of the C plus plus compiler in the runtime system, anything may happen. You may either get an immediate error flag. So, that is called early error detection or the compiler may be happy to just access some arbitrary memory location which is outside the control of the array. We will get garbage. If you try to read garbage, you will just get garbage, but if you try to write an element of an array which is beyond its legal limits, you may end up writing other critical parts of your code or data and your program may crash in completely unpredictable ways. So, this is one of the greatest fears in implementing C or C plus plus code without a protected memory layer. If the memory is just a one-dimension and homogeneous series of bytes and the memory itself has no knowledge of what cells are part of an array and what cell is part of my control code, then a programmer is free to overwrite anything. So, be very careful about index arithmetic and make sure that your indices do not walk outside the legal limits of the indices 0 through n minus 1. So, how do we declare a native array? Here is the very first code where main starts and we have to say that the size of the integer is say 9. So, this vector or array will call all-dimensional arrays as vectors also has 9 elements. So, that is V n and then we declare the array using this notation that int V a box open V n close box. So, to the system this means that V a is no longer a scalar variable with one integer in it. It is actually a collection object. It is an array with 9 slots in it. The slots are numbered 0 through 8. Now, one contrast with the vector class is that when the second line is executed the C plus plus runtime system will allocate 9 integers, name them as V a we shall see how, but after that it will forget the range of memory cells which stores V a. So, the knowledge of V n equal to 9 has to be stored in your code only. The system has no further memory as to how long V a is after that point. You have to make sure that V n is preserved and the index access to V a is always between 0 and 8 inclusive. The vector class on the other hand will remember the size with which you allocated the vector and it will check for out of bound exceptions. Now, how do I initialize this? Suppose I want to initialize this to some kind of arbitrary values then I can say for int V x equal to 0 V x less than V n plus plus V x V a of V x is equal to V x in to say something. So, it will be something like a parabola. So, the number of elements in the array to be created is provided ahead of time. In fact, it is much safer to declare that as a const if you can because if V n changes the array itself will not change. The array allocation has been done once and for all. The array is exactly 9 elements long. If you now override V n to 12 that is very dangerous because you know you can walk past the original end of that. So, do not do that. So, that statement deserves memory for a 9 element array and when I initialize into it the left hand side expression in red on my projector which looks like a right blood V a of V x. So, that is called an L value. It is a value which appears to the left of an expression and in that context it means that it is a cell to which the right hand side value is to be written just like in characters in strings. So, V a of V x is the cell in which the right hand side value is written. Whereas, if I want to print out the array I will say c out less than less than V a of V x. In the green context V a of V x is called an R value because it appears to the right of some expression. And there it actually accesses the integer in the specified cell and prints that integer or uses that integer in an expression. So, L value and R value are distinct uses of an array. See in case of standard scalar variables I only had the name of the variable to the left or to the right. Even there there are different connotations to the right if there is a scalar variable called say age or blood pressure or just access it from the cell and use that value. If it was age equal to something that means that right into the cell corresponding to age. It is the same here except that the cell in memory is identified by two things the name of the array and the index in there. How that is done we will see in the next slide. So, if you want you can print commas between the elements and then print a new line at the end and observe that there is no size or length in case of native arrays unlike strings or vectors. So, you have to hang on to this variable V n and try not to change it. Now this is how an array of integers will be represented in memory. Elements of arrays for now will have a fixed size because there will be primitive types. Element of an array will be an int or a short or a Boolean or a float or a double. So, every double has the same size and that makes things easy for the compiler to deal with. At the point that we would like to store unequal size things as elements of arrays we definitely want to get into another vector class. We will see that a little later not today. Now these are laid out consecutively in the sense that the first element of V a which is V a 0 start at some memory location say whose address is a. And then that consumes 1 32 bit integer or 4 bytes. And then immediately after that the next element of the array V a 1 starts and that takes 4 more bytes. Suppose the first number is 0, the second number is 7, the third number is 12 each of them take up 4 bytes consecutively in RAM. And V a of 2 starts at this byte number 0, 1, 2, 3, 4, 3, 4, 5, 6, 7, 8. So, to fetch V a of I x where I x is a legal index we go to the byte address corresponding to the V a or a plus I x into 4. So, this should be capital A. We will see this in code and we will see how this executes. So, because each element is 4 bytes long to go to element I x you have to say I plus A plus I x into 4 you do not do it the compiler does it. And then you fetch the next 4 bytes 7 integer. So, the base addresses in memory for the first few elements are A A plus 4, A plus 8 and so on. That is how an array is organized no one except you is in charge of deciding where the array V a will end. In most C compilers or C plus plus compilers if you access V a of 1 million and that is way past your declared array length. If it is within your process address space the compiler will happily provide you a junk value from some arbitrary place in memory you do not even know. Whereas, in Java will throw an exception because Java will remember the original allocated length just like vector and it can use that to you know through an exception if you try to access something out of bounds. So, let us see what these things look like in code. I will try to print out some internal things if we can. So, I have here one array which is declared. So, do not look at the AV part now. Let us look at just the B part. So, I declare a length of B as a constant which is 10 things and then I declare this integer array BV with size BN. At that point the array will be uninitialized. In Java if you do this in a suitable different notation you are guaranteed to get zeros in the array. C or C plus plus for efficiency reasons do not initialize it at all. Whatever the state of the memory is may be you know some other process exited and you inherited that space some garbage will be there. So, to use this correctly you always have to initialize the values. Then initialize as I was saying BX equal to 0 through BN minus 1 some arbitrary expression which looks like a parabolic function. Now, I print out the same thing and then at the end of printing out each things without a new line I finally print a new line, fine. Let me try one more thing which is I will just print out BV itself just like that without printing out BV of an element I will print out just BV and see what the system gives me and I will comment this out. Yes, I mean you could combine the two, but generally speaking you do some operations here, right. So, if you are initializing and printing it immediately then you do not even need an array. If there is nothing no other piece of work being done otherwise you work on the array and then you print out again in a following. And just for testing I will print out BV itself to see what happens, ok. Now, I compile this I run that. So, you see that initialization is like a parabola. So, at the beginning it is 0 and then you know 9, 16 etcetera coming down to 9 again it is symmetric about some point, ok. And then when I print BV itself without any qualification it prints this crazy hexadecimal number, ok. This is that base address A, ok. Remember your address itself is an integer, it is a count of a byte inside your RAM, it is a location number, ok. That is the location number. So, this number is always printed in hex because in the system base 10 does not mean anything usually base 2 is good base 2 take 2 large to print. So, the convention is addresses are printed in hex. So, that is the base address, ok. Now, to give you just an indication of why this address scheme is used suppose I do something like this. So, if I say BV plus 1, ok that actually although you should never use it BV plus 1 means the base address of the second element of the address. BV plus 0 is the base starting point of the address, BV plus 1 is the next element, BV plus 2 is the next element and so on. So, let me try to compile this and see what it prints, ok. See what happened here. I had 9 C and it became A 0, ok. So, let me write those down and see what that means. So, there is a bunch of digits which we will ignore at the end there was 9 C same digits and A 0, ok. Do you remember what C is? So, remember decimal 10 is A, 11 is B, 12 is C, right. So, what happened is the array started at 9 C, ok and then D E F. So, C D E F these 4 bytes comprise the first element and the second element started from therefore, 9 C plus 4 which was the next position. So, 9 C plus 4, C plus 4, C C plus 1, C plus 2, C plus 3, C plus 4 is 0 with a carryover, 9 plus 1 is A, right. So, that solved the second position in the array is that address this. So, I just printed out the BV plus 2 that became 820 to 824, ok. So, another way to write this is to say what is the address of BV box 1 and this is the same notation for that. We shall see all this later on do not worry about this the operator for an address is ampersand and you can say BV 1 like that. So, what is the address of BV 1, for address again C plus plus uses the AND symbol. So, see the problem is they overload all these symbols to mean very different things in different context single ampersand, double ampersand whether it is used in a binary operator context or a unary operator context you know the meaning changes just to compile this and run it will print exactly the same thing. But did you observe what happened here? I just did two different runs the address changed ok. However, the difference will always be 4 from one to the other. So, you run the code again the operating system allocated a different patch of memory to hold this array. So, the absolute address changes, but the relative offsets between different elements will remain the same. So, you should never bank on finding a specific address to be at the beginning of the array that could be placed anywhere in memory. So, that is internally how arrays are implemented that is the plumbing for the arrays, native arrays. So, is this absolutely clear how arrays are implemented. So, the summary is that an array is one name which refers to a collection of values the values are stored in cells which are stored contiguously in basic arrays of native data types each cell has the same length in bytes and given a array name and an index you can always do a basic multiplication to decide the starting byte address of every element. And also C plus plus and C are sloppy about remembering the length of the allocated array. So, you have to take care of it. So, to give an example again of that remember this array was of length 10. So, suppose at this point I suddenly decide to output let us see what happens BV of 20 clearly there is no such thing as BV of 20 I still compile this I find some garbage there right next time there is a different piece of garbage there. On the other hand the thing that you initialize that is not garbage no matter where the memory is allocated you have filled it with the same value. So, that is reliable if you access something which you did not initialize that is a problem. The worst thing would be if you try to write BV of 20 you may not even own BV of 20 that may not be legally in your address space. Good operating systems should prevent you from writing other people's memory, but it is yes it will start from the same place it will access. So, in this case it will take 20 chunks the original allocation was only for 10 yes, but beyond that there is always some system memory that is right yes. So, to give you an impression. So, suppose I first print out the address and then I print out the value. So, if I compile that. So, these two differ by 20 you can verify that yeah they will differ by 20, but the value you get here is not something that you own. So, you can get some arbitrary garbage. So, the worst thing is to write into BV 20 something I imagine Linux is good enough to prevent you from writing dangerous areas of RAM, but because I do not want my computer to crash in the middle of the lecture I am not going to write into BV 20, but you can try yes. So, OS's own RAM should be protected typically what will happen is your own RAM if you also allocated that chunk of RAM it belongs to you, but it is improperly or not initialized you will be allowed to write it. In some cases you can clover your own control structures. So, your code can crash in some architectures. It should not in good operating system it should not it should be isolated. So, this is this is how arrays are implemented and so you have to be careful about the index and where you are accessing memory. So, now again let us go through a number of examples of how to write basic operations on arrays. So, remember 1D array vector we will use interchangeably. Suppose ARR is an array which has size a n. Suppose you want to find the sum and product of all elements. So, by now it should be quite trivial. So, I say for integer a x which is the index between 0 and n minus 1 I can say sum plus equal to array a x product star equal to array x. In standard arithmetic mathematical notation we will write this as sigma i equal to 0, but less than n either in the sum of a i or product of a i. So, what used to be this small line of notation becomes a small piece of code. Now, if you were doing a complicated piece of numerical calculation you might want to find sums of very many different arrays or maybe you have a big matrix and you want to find the sum of every row. You do not want to kind of create tested loops all the time or you want to rewrite this code for every new vector that comes in. That is why we write functions that is why you can pass an arbitrary array into it and it outputs the sum of the product of all the elements. So, that we can reuse that piece of code. So, we will see that when we start writing functions of our own. How do you find the dot product of two vectors again should be fairly easy the formula itself is quite clear i between 0 and n product of a i and b i that is a dot product. So, I initialize the two arrays a v and b v a vector and b vector and I also initialize the output dot product to 0 and you know I just run basically parallel through the two arrays and multiply. So, also observe that you need the proper initializers product should be initialize to 1 sum should be initialize to 0. So, what if I want the cosine of the angle between the two vectors. So, in that case you need to normalize by the length. So, if I want the cosine of the angle between two vectors the formula is the dot product divided by the two norms of the vectors. So, I can do it this way I again initialize I start the same loop i x equal to 0 through n n minus 1 and then I compute a contribution to the dot product which is a v of i x times b v of i x, but I also build up the norm expression. So, I say a norm plus equal to a v i x square and b norm is plus equal to b v i x square at the end of the computation. Therefore, I have the quantities available are the numerator and just the stuff inside the square root of the denominator. So, finally, my answer could be written as dot divided by square root a norm divided by square root b norm. So, in this particular case I might easily have declared three for loops and contributed computed everything differently separately from each other, but setting up a for loop it is initialization the variable management memory allocation for i x and at the end of it to tear down that variable space that takes time to save that whenever you can it helps if you put all the computation you can do in one loop provided they do not interfere with each other. As we have seen already sometimes unless you declare temporary variables there are these loop carried dependencies. Luckily in this expression there is no loop carried dependency a single iterator i or i x can compute all the three required sub expressions side by side. So, therefore, in such cases it will save you a little bit of time if you run the accumulation of values parallel inside the same loop. So, this is how you compute angles between two vectors. It turns out that this particular operation is very important in a lot of high performance areas I mean dot products are very significant things. In particular when you do a Google search one of the very significant indicators of how relevant a document is to your query is to regard your query as some kind of a vector in a very high dimensional space where every dimension is a word in the vocabulary. So, every document is represented as a very sparse vector in a huge dimensional space your query is a vector in a huge dimensional space and you compute the dot product of the cosine between the query and the document that is one of the principal signals in deciding how relevant the document is. So, if your query and document shares a lot of words then of course, the document is a priority more relevant. Now, there the interesting thing is how do you compute this over billions of documents and your query in less than a few milliseconds and that is a interesting challenge which we take up in other courses in CSE. More examples how do you calculate the smallest and largest elements of an array. So, to get started we will assume this convention that if I give you an empty array with no elements then the largest element is minus infinity and the smallest element is plus infinity. Sounds the opposite of what we might instantly expect, but this is the correct way to go about things. Later on we will show how to instantiate to plus infinity and minus infinity for all the numeric types for today do not bother about it. Suppose you have predefined constants called plus infinity and minus infinity standing for plus and minus infinity. So, again here is an array with length a n which is suitably filled in and we initialize minimum to plus infinity and maximum to minus infinity and then we iterate through the array int a x equal to 0 to a n minus 1. We will look at the element which is array of a x that is an int and then minimum is this conditional expression we have already seen that. So, minimum is if the current element is less than minimum then it should be element otherwise it is minimum and maximum is if the current element is greater than maximum then it is the element or it is maximum. So, what is happening here we start off with focus on one of them say the minimum element min. Min is initialized to plus infinity when I get the first element of the array that is always less than plus infinity typically and what will happen is at the first iteration min will be overwritten by the first element in the array. In the second iteration if the second element is less than the first element then min will be reassigned otherwise I will retain the first element that is how min will be generated and then max will be the opposite of that. So, let us run this through our earlier array and see. So, I have already initialized this array instead of I can print those out and then let me add the min or max code I will just do the min first for clarity. So, for today I do not quickly have access to plus infinity. So, what I will do is I will initialize this to 2 to the power 32 minus 1 or 2 to the power 31 minus 1. So, the one of the easiest to write that is shift 1 left 31 times and then minus 1 that is the maximum possible integer. The other thing you could do equivalently would be to look at the minimum integer which is 0 x 8 or if it is an int I can do 7 f f f f f f that is also plus infinity that is the only plus infinity integers can represent after which you run this through I will just do min I will take off the max. So, you can trace it nicely. So, let me print out the following. So, this is min so far running minimum. So, I initialize minimum to plus infinity effectively and then I keep resetting minimum to the minimum of element and minimum itself. So, if element is lower than minimum I will set it to the element something strange happen oh a x and a n instead of array I have b v. So, first it is prints the array and then that junk. So, the element is 0 and the minimum is now come down to 0 minimum used to be plus infinity right and that is it minimum never changes the first element is the minimum. So, now let us also put in the maximum max is set to minus infinity. So, that 0 x 8 that is minus infinity and then max is set to element if element becomes greater than maximum then I have to reset my maximum otherwise I just look at maximum. So, again I print out the maximum. So, what happened here after reading 0. So, maximum started out being minus infinity. So, it I may see a 0 maximum goes to 0. In the next iteration let me switch off the minimum print out to make things clear. So, element is 0 maximum is 0 it jumps up from minus infinity to 0, element 9 maximum is 9 goes from 0 to 9, element is 16, 16, 21 tracks up to this point 25, but then I get element 24 max does not decrease anymore. So, max stays at 24 to the end fine and if I print those two values at the end I get 0 and 25 the smallest and largest values. So, clear what is going on. So, this is how we get the smallest and largest elements. What if there are ties do you care if there are ties well there was a tie in this particular case we seem not to care there is already a bunch of ties in the elements. So, here is 16 there is 16, but the maximum and minimum were the same. So, let us try one thing let us change this to maybe an odd number see what that does say 9. See the advantage the other piece of advice is that you never want to you never ever want to write a piece of code where you declare an array with a constant number inside there. You always make it declare it as a variable add and use the variable because some day if you do that you are going to change this number and forget about the limit somewhere else. So, your code should never have bear constants like that in there bear constants like minus 1 and plus 1 you will need for array index manipulation, but those are relative constants. You should never decide on the size of data structures inside your source code as far as possible if you do have to do that then you do it in one place. So, for example, here I change 10 to 9 and I am pretty sure that I have not done anything specific to 9 in anywhere in the rest of the code that is very important. Otherwise someone else will modify one part of the code it will be inconsistent with the number somewhere else and then you will be walking outside legal RAM and so on. So, see what I did here is that this 9 the size of the array appeared in exactly one place with one variable named bn. Nowhere else I used the property of 9-ness without referring to bn. Anywhere else 9 was used it was used as bn. So, therefore if you want to change the size of your array tomorrow to address a larger smaller data set you can just change one place and be confident that no code will misbehave. So, never declare bare literals in your code which correspond to data structure sizes. Bare literals will appear in array index arithmetic that is because those are relative. We will see some examples of that. So, if I change the array size to 9 and then I say compile this now what happens? See there are two maximums now. There is only one minimum 0, but the maximum is now at two places and maximum is found properly. So, what happens in the code is that see max as the element is greater than max. Once max has been assigned to 20, the next max is ignored because it is not greater than 20 strictly. That will do your trick. So, in general duplicates are not really a problem in this game. If there are ties so be it. We might want to remember however the position or the index of the smallest element suppose we want to do that. So, given a double array between 0 through n minus 1 suppose we say find the index i min where the smallest element lives. So, this time I must remember the current smallest position as well. It is not enough to just remember the smallest value min. I have to also remember the i min which is the index at which the minimum value appears and I can start it off with any illegal value like minus 1, minus 1 is not a legal index to any array it starts at 0. So, you can start it off with minus 1 and then of course, also of double a min which is plus infinity and then I do the following loop I say for int a x equal to 0 a x less than equal to a n minus 1. If the array element is less than the currently known minimum value then reset the known minimum value to the current array element and remember the current index a x in i min. It is just one more variable and setting it every time your minimum is updated. So, and then finally i min will hold the answer. So, let us go back to our code and run it there. If your array turns out to be empty then you might want to signal that with a legal value at the end. So, suppose so in this case this spiffy conditional expression is not that much used because you have to update another variable anywhere. So, in that case you can just write if let us print out the current position a x I keep on using java notation and the element the minimum and the position of the minimum and we will forget about the other print outs. So, a x is 0, element is 0, minimum is 0, i minimum is 0. So, nothing changes. So, it is kind of boring. How about the max guys? So, let us say i max is again minus 1 and if element is greater than max. So, I have max equal to element i max equal to a x. So, I will kill that and I will write the max value and the i max value. So, all I did is flip the sign around and I am just tracking the maximum guy now. So, I compile that and see what happens is max keeps updating because the values keep increasing up to this point. So, i max goes up to 4. So, this is 0, 1, 2, 3, 4 and then because I do not replace 20 by 20 it does not change to 5. This is the first position of the largest value. So, this here you get the left most position of the largest value in the array. So, i max will increase to 4 and then stay put at 4 and max will stay put at 20. So, that is how you calculate the maximum value of an array and its position. Now, here is the next step which is suppose I have found the smallest element in the array. I can swap it with the first guy. Once we find I mean we can exchange the element at I mean with the element at 0. So, we do that very simply by saying double temp equals and array 0 is equal to temp. So, this places the smallest element in slot 0 and its earlier contents are arbitrary plus in the array. Now, all we can even push the largest if you wish and if you keep doing this we should get a sorted array. So, let us look at that code. So, we already have code here to find the maximum position i max. So, let us swap it with position 0. So, let us say in temp equal to b v 0, b v of 0 equals b v of i max and b v of i max equal to temp, fine. After that we will print out the whole array again. We will omit this long print out in the middle because we already seen what that does. We will just print out the whole value of the array after we do that. So, we will just print out there after we do that swap here. So, the summary is the first loop detects the largest element and its position then this which is the largest element to the first position the 0th position and then we print out there. So, this is pretty trivial. So, if I do that and run it. So, look what happens this 0 and that 20 has been switched the 0 has now gone here the 20 has come here fine. Now, I can keep doing this I can always I can pick the largest element from the second onwards and bring it to the front again. So, this is how it will go. So, let me write that new loop. So, I have initialized it. Now, let me keep that around. So, I can look at it later. So, let us say I start my scan see what happens is let me write down the plan here. So, here is my array to start with I start scanning from this and go to the end I find the largest element and the position of the largest element. Suppose that was here then I do the swap and this becomes the largest. Now, I say start here and do that same thing and then do that switch. So, let us say this index let us say I say first x. So, first x will itself go from 0 to. So, I will say for f x equal to 0 through n minus 1 etcetera. Now, I will say find maximum element and position between f x and n minus 1 inclusive and then move or swap f x to 0. So, I will write it down here. So, that I can switch off that board. So, the broad outline is for first x equal to 0 through n minus 1 find max and I max over positions f x and max through n minus 1. So, I have contents of f x and I max that is the broad outline. So, writing that is no big deal. So, let us say for. So, set up the f x loop in that I now need to find the maximum guy and the position of the maximum. So, I cut and paste this inner loop. So, I do not need min let me switch that off I max is equal to minus 1 max is equal to minus infinity. Now, I scan where do I scan from I do not start from 0 anymore I start from f x and then I go to the end of the array and I look for the maximum and remember the maximum position as usual. So, now I can this ends and that ends. Now here I have to do something at this point I know you know I have I max correctly initialized. So, here I swap and f x. So, how do I do that I just cut and paste this code now this is not 0 anymore I was just doing f x right and then b v of f x is I max and. So, that swap remains and I do not want to only print it at the end I am going to say see what happens after I pull up each of the top elements to the top to the left fine. So, the first part of the loop is find position of max between f x and b n minus 1 then swap I max with f x and print and then I f x will automatically be incremented here. So, let us compile this and now if I run that. So, the original array was this order of the first step this 20 comes to the front after the second step that 20 comes to the front then this 14 comes to the front sorry this 18 comes to the front and then at the end of it you see that the first so many positions are in sorted order the largest things are coming in and. So, at the end of it you get the 20s 18s 14s 8s and 0s. So, this has now become sorted by decreasing order right inside the for loop because see every time I finish that I have to swap f x with the current maximum yeah by a factor of 2. So, that something I want to discuss next which is what is the total time taken in this operation. So, see the when you when you read this kind of code you should look for how the how many times each loop is executed. Now what happens is the outer loop clearly goes from 0 through n minus 1. So, that is about n steps and then inside to detect the maximum I have to run another loop which in the which also goes from 0 through n minus 1 and then the swap itself takes constant time. So, the total time is n squared. So, total time taken by this algorithm will be n squared now how do we know how bad that is. So, let us try the following. So, I will start off with say 100 elements and let us time that user time is just 8 milliseconds I will shut off the print out see the eventually the output is sorted it is kind of all kinds of weird numbers. If you want to read in slow motion pipe it through less initially the number is kind of bizarre some arbitrary order this is after the first step not this 250 is the largest value it came from somewhere inside otherwise it was monotonically increasing and then decreasing because of the quadratic form of the initialization and then 2499 came after that and so on. And anyway again the time taken was say 8 milliseconds. Now, if I claim that the time taken is n squared and it used to be 8 milliseconds adding a 0 here should give me 800 milliseconds roughly. So, I will compile it again and if I time this printing takes some different amounts of time depending on system problems. So, let me shut off the printing see it is misleading because it is taking time for other kinds of junk. So, let me kill that. So, I will cancel this printing and only look at the time how did that happen. See earlier time was taken with the prints now that I have cancelled the prints I should restart from the old code. So, printing takes a lot of time lot more than CPU work because display is slow. So, if I take 100 elements it may be in fact be 2 small 2 meter 0 I can sort a 100 elements without any time that is not true of course. So, let us start with 1000 see how long that takes. So, 1000 now takes 8. So, there is a granularity below which the system does not even measure very properly. So, let us say 1000 takes 8 milliseconds if I make it 10000 and compile it there is something else going on this also has to be cancelled even printing a new line takes time. Let me go back and switch this to 1000. So, this is another thing which is in measuring time you have to be very careful later on in the course will have a few comments on measuring time accurately. So, now it claims 4 because that new line printing is gone. Now, if I do 10 times and compile again let us say about 800. So, this 4 is too small to measure properly. So, let us stick to say 0.8 seconds. So, suppose I had about 0.8 seconds and now suppose I double this then the time should be 4 times about. So, let us compile this it strictly not it could be slightly more than quadratic because of the swap time and all that stuff. So, there is a little bit of linear component also it is not strictly x square it is x square plus some a x that is the time taken. So, we took n elements and we wrote our routine to sort it which was taking about n squared steps. Now, natural question to ask is how fast can you do it anywhere is it possible to sort much faster than n squared steps and here is one kind of argument. So, see every time I am comparing two elements a i and a j that is the unit of decision a comparison can go one of 3 ways less equal to or greater than for simplicity assume that all the elements are distinct. So, you never compare equal unlike in my example just imagine they are all distinct elements. So, every comparison gives you two possible outcomes. Now, you could think of some arbitrary piece of sorting code as making a kind of decision tree saying I am going to compare two different elements either it is going to land up less than or greater than then I am going to strategically choose two other elements I am going to compare those and depending on that I will do something. In an abstract sense any sorting algorithm is doing this it is comparing two elements at a time like we are comparing minimum or maximum with some element and that is deciding what to do based on that. So, every decision increases the size of your space by a factor of 2. So, if I so in any particular execution of the code you run down one path depending on what value you see you take the suitable steps. So, if I take L steps clearly the number of possible outcomes you can handle is 2 to the power L, L is 0 here, L is 1 here, L is 2 there and so on. Now, to sort n elements what is the possible number of sorted orders of the n elements n factorial right. If the original numbers were in some arbitrary order their sorted order could be any of the n factorial possible orderings. So, to be able to handle all possible permitted inputs I must have that 2 to the power L is about equal to n factorial otherwise I cannot handle all the situations. Now, if you solve for this you will basically get L log 2 is equal to log of n factorial which is about n log L. So, this tells you that algorithms which use comparison between elements to sort they cannot possibly finish sorting in less than about n log n steps. This is independent of what algorithm you have I am not making a judgment about a specific algorithm that is called a lower bound no algorithm can be better than that. Meanwhile, we had an algorithm which does n squared steps. So, clearly there is a gap between n squared and n log n and in the next week or week after we will look at more smart sorting algorithm which actually reach this bound and they can sort within n log n steps. To give you just one idea about how that can be done you can say pick an arbitrary element in the array find all elements which are less than it and find all elements which are greater than it. Put all the less elements on one side put that middle element and put the right element and now keep doing that. Now, take the left part which is not sorted take the right part which is not sorted. So, that algorithm actually achieves n log n. So, we will see all that later on in the course. Now, let us go past to look at yet more examples of how arrays can be useful. So, any questions about finding the position of the smallest or largest element and then remembering the position and using it to swap it. So, that you get sorted arrays. So, just to write down a short somebody here is an array. I know how it is laid out in RAM a 0 a 1 etcetera. I know how to write this expression or I can use a as a r value. I know how to pick the minimum and maximum from an array. I know how to remember their positions. Once I know that I can take the maximum and do a swap with the first position and then repeat from the next position and that gives me a sorted array. So, we will actually go back to simpler examples now. So, here is one problem where I want to compute. So, given an array a between 0 and n minus 1, I want to compute another array b where b i is the cumulative sum or the prefix sum from a 0 through a i. Now, clearly I can do it the expensive way which is a double loop. So, the double loop will look like this. So, the problem statement is clear. So, suppose I have my old array a which is equal to say 0 9 2 5 6. I want my b array to be 0 0 plus 9 because 0 plus 9 plus 2. So, the silly way to do that would be to say for b x equal to 0 through n minus 1 says sum equal to 0. Then for a x equal to 0 through b x sum plus equal to a a x and then finally b b x equal to sum. Surely that will do what I want, but clearly that is not necessary. So, this is why. So, you can clearly see that b 0 is of course equal to a 0. So, I can copy it straight, but when I want b 1 instead of taking a 0 and a 1 I can take b 0 and a 1 and that will give me a 0 plus a 1 effectively. When I want the next value because I already have a 0 plus a 1 I should be reusing it and just adding on a 2 to get a 0 plus a 1 plus a 2. So, and keep doing that. So, therefore, I can instead write the code more efficiently to take linear time instead of quadratic time. So, computing a prefix sum is linear time and you cannot be better than that that is kind of obvious. So, vector a x is filled properly to start with and then I start this loop i x between 0 and n, where I initialize you know I set b of i x to be equal to in general b of i x minus 1 plus a of i x. The only exception is in filling the first position where there is no minus 1. This will show an error if i x equal to minus 1 or it will get garbage and that corresponds to this initial situation. So, the right most position will not give any trouble the left most position will give trouble because i x is equal to 0 and i x minus 1 is equal to minus 1, which is that illegal value is trying to access from the left question mark. So, to handle that we will help it out we will use this conditional expression where we say if i x equal to 0 I will supply a 0 as a base case because then the sum will be turn out to be correct. So, the first b 0 will be computed as 0 plus a 0. So, a minus 1 is implicitly defined as 0. There was no real a minus 1 I supplied the value from the side. So, if i x equal to 0 then I get 0 otherwise I get b i x minus 1. So, that red part of the expression here corresponds to that red question mark with the first error. So, let us write the code for that again. There is always an app for that. So, I will comment out the rest to avoid distraction. So, after printing out the array. So, here is the original array. So, now I am going to do a prefix sum. So, I allocate a new array say int prefix vector b n and then I do for int p x equal to 0 p x less than b n plus plus p x. What was the code again? The code was the prefix vector is this. So, let me just copy and paste that. So, this is actually p v of p x equals if p x is 0 then I supply a dummy value of 0 from the left and the stuff I am adding up is p v p x minus 1 plus b v of p x. So, and then I will print out the two let us say one after the other. So, here I will print out p v instead b v and p v. So, b is the original vector in the code and p is the prefix sum. So, the important loop is this one. I declare the output prefix vector as this array and then at every position I look at the prefix or the previous position plus I add in the value at the current position. If the current position is 0 then I cannot go left of 0. Therefore, I supply a 0. So, if I compile that and run it 2 big 2 big we can probably handle 10 elements. So, the original elements were 0 9 16 21 etcetera. So, 0 is 0 this 9 is 0 plus 9 this is 9 plus 16 0 plus 9 plus 16 that is 0 plus 9 plus 16 plus 21 and so on. So, there is no need to use quadratic time to compute a prefix sum. You can reuse the last value and keep tagging on the next number that is the summary of this segment. So, why are prefix sums useful? Of course, when you look at your pass book in a bank that is arranged by time and withdrawals are negative and deposits are positive and the balance is always a prefix sum from time you know minus infinity or whatever when you open the account. So, the bank maintains a you know has to calculate a prefix sum to print your pass book. It is also useful for what are called range sums. So, given an array preprocess it. So, that given a query which is a range from i to j return the sum over that range very quickly. Why is that done best by prefix sums? See if you have computed the prefix sum up to i and you have computed the prefix sum up to j then clearly the sum between i and j is just the difference between those two prefix sums. So, instead of actually spending j minus i times summing of those things if I have already recorded the prefix sums at i and i at j I just need one subtraction. So, given an example here you look at this printouts. So, this is the original array that is the prefix sum. So, if someone tells you asks you what is the sum between positions 1 and 3 0 1 2 3 the sum between this is 9 plus 16 25. So, the sum is 46 between these locations. Let us not take the 0 let us take something else let us say the sum of these two numbers 16 and 21 right that is 37, but 37 is also 46 minus 9. So, to calculate the sum of any contiguous segment here take the corresponding number here and subtract the number 1 position to the left of the left point. So, that is a very fast way of computing the sum of a segment and that is very useful in some applications you will see that in some exercises. You have to calculate the prefix sum ahead of time before the queries arrive and that takes linear time. Yeah, sure. The point is you preprocess once, but then you could answer everyone's query in constant time. The preprocessing does not depend on the query the preprocessing is done only once you just calculate a different time. In fact, as you can figure out there is no need to preserve the original array. You can reuse the original array to compute the prefix sum and you can always recover every number from the prefix sum by subtracting the previous number. So, in the same space you can rewrite the prefix sum and then you can solve range queries like this. So, what is the time now? It is 25. So, I will stop here and next time we will take up this interesting problem called the Josephus problem where people stand in a circle and then you count every fifth person and remove them from the circle and see who survives to the end. We will see how to solve that using arrays.