Introduction to main concepts of algorithms: Correctness: the algorithm does what it is supposed to do It should not be taken for granted that your algorithm does what you would like it to do. Correctness (the property of being correct/ doing what it is expected) is not a trivial property. It needs to be carefully thought through and you should be able to prove that your algorithm is correct if requested to. One way of doing so, when possible, is to use a loop invariant. [There are other ways, including using constraints – see work done by Shubhra Datta at UTEP for her Master’s thesis] Example: insertion sort. How does it work? Just like when we sort our cards. Input: array A of n numbers to be sorted [it does not have to be numbers… just anything that belongs to a set that can be ordered] Output: an array of the n same numbers but in non-decreasing order (modified array A) For j from 2 to n // we are ranging from the second number to the end, since a single card is sorted // already Key = A[j] // we keep the number to sort in memory so that we can overwrite it in the array // now we are going to insert value Key in the sub-array A[1..j-1] i = j-1 // i is going to keep track of the index of A[1.. j-1] that we are considering // going down from j-1 to 1 (if necessary; maybe we will stop before) While i > 0 and A[i] > Key A[i+1]=A[i] // we move the value of A at index i to the next one… creating space for Key // in case it would be greater that A[i-1] i = i-1 // once we are done comparing Key with A[i], we move to the previous item in A // once we are done comparing, it means that: // either: we have reached the beginning of A[1..j-1], in which case, A[1] is now assigned to // location 2 in A and Key needs to fill A[1] // or: A[i] <= Key, which means that Key should be inserted at index (i+1) // in either case: Key is assigned to A[i+1] A[i+1] = Key Loop invariant: definition. Initialization: It is true prior the first iteration of the loop Maintenance: if it is true before an iteration of the loop, it will still be true at the end of it, which is before the next iteration Termination: when the loop terminates, the invariant gives us a useful property that helps show that the algorithm is correct Comment: it is very similar to induction Base case = initialization Inductive steps = maintenance Proof = termination In the case of insertion sort: Initialization: before the first iteration, the array of 1 element is sorted = A[1..j-1] Maintenance: if A[1..j-1] was sorted, after iterating through the loop, A[1.. (j+1)-1]=A[1..j] is sorted Prove it by also showing the loop invariant of the inner while Termination: A[1..(n+1)-1] is sorted Exercises: (not to be turned in) 1. Illustrate the operation of Insertion sort on the array A=[31,41,59,26,41,58]. 2. Rewrite the above algorithm to sort in non-increasing instead of non-decreasing order (see solution below). For j from 2 to n // we are ranging from the second number to the end, since a single card is sorted // already Key = A[j] // we keep the number to sort in memory so that we can overwrite it in the array // now we are going to insert value Key in the sub-array A[1..j-1] i = j-1 // i is going to keep track of the index of A[1.. j-1] that we are considering // going down from j-1 to 1 (if necessary; maybe we will stop before) While i > 0 and A[i] < Key A[i+1]=A[i] // we move the value of A at index i to the next one… creating space for Key // in case it would be greater that A[i-1] i = i-1 // once we are done comparing Key with A[i], we move to the previous item in A // once we are done comparing, it means that: // either: we have reached the beginning of A[1..j-1], in which case, A[1] is now assigned to // location 2 in A and Key needs to fill A[1] // or: A[i] <= Key, which means that Key should be inserted at index (i+1) // in either case: Key is assigned to A[i+1] A[i+1] = Key 3. Show that this algorithm is correct using the loop invariant technique. Hints: see notes above + invariant of the inner while loop: A[i+1..j] is sorted 4. Exercises 2.1.3 and 2.1.4 of Introduction to Algorithms 2.1.3: for i in 1 to n: if A[i]=v, return i return nil loop invariant: v has not been found in A[1..i-1] 2.1.4: integer add = 0 for i in n down to 1: If A[i]=1 and B[i]=1,then C[i+1]=0 + add add = 1 if (A[i]=1 and B[1]=0) or (A[i]=0 and B[1]=1), then if add = 1, then C[i+1]= 0 and add=1 if add = 0, then C[i+1]=1 and add=0 if A[i]=0 and B[i]=0, then C[i+1]=add add = 0 C[1]=add