=========================================================================== CSC 236 Lecture Summary for Week 7 Fall 2007 =========================================================================== - Recursive binary search: RecBinSearch(x,A,b,e): 1. if b == e: 2. if x <= A[b]: return b 3. else: return e+1 else: 4. m = (b + e) / 2 # integer division 5. if x <= A[m]: return RecBinSearch(x,A,b,m) 6. else: return RecBinSearch(x,A,m+1,e) Precondition? Elements of A comparable with each other and x, 1 <= b <= e <= length(A) (assuming array indices 1..length(A)), A[b..e] sorted in nondecreasing order (A[b] <= ... <= A[e]). Postcondition? RecBinSearch(x,A,b,e) returns index p such that: . b <= p <= e+1; . if b < p, then A[p-1] < x; . if p <= e, then x <= A[p]. Proof of correctness: By induction on size n = e-b+1, prove (precondition and execution) implies postcondition. Inductive structure of proof will follow recursive structure of algorithm. Base case: n = 1, i.e., e = b. Then, algo terminates (lines 1-3 contain no loop or call), and returns b if x <= A[b], e+1 if x > A[e], which satisfies postcondition. Ind. Hyp.: Let k > 1 and suppose postcondition holds after execution for all inputs of size j that satisfy precondition, for 1 <= j < k. Ind. Step: Consider call RecBinSearch(x,A,b,e) when e-b+1 = k >= 2. Test on line 1 fails, so b < e (since b <= e by precondition and b =/= e by negation of test) and algo executes line 4. [Exercise: prove b <= floor((b+e)/2) < e for all b < e.] Next, test on line 5 executes. Case 1: x <= A[m]. b <= m < e -> m-b+1 < e-b+1 so by IH, RecBinSearch(x,A,b,m) returns index p such that: (1) b <= p <= m+1; (2) if b < p, then A[p-1] < x; (3) if p <= m, then x <= A[p]. Hence, . b <= p <= e+1 (from (1) since m < e); . if b < p, then A[p-1] < x (from (2)); . m < e -> m+1 <= e -> p <= e, so we must show x <= A[p]: o if p <= m, then x <= A[p] (from (3)); o if m < p, then A[m] <= A[p] (A is sorted) so x <= A[m] <= A[p]; in all cases, x <= A[p]. Therefore, current call satisfies postcondition. Case 2: A[m] < x. b <= m < e -> b < m+1 <= e -> e-(m+1)+ < e-b+1 so by IH, RecBinSearch(x,A,m+1,e) returns index p such that: (1) m+1 <= p <= e+1; (2) if m+1 < p, then A[p-1] < x; (3) if p <= e, then x <= A[p]. Hence, . b <= p <= e+1 (from (1) since b < m+1); . b < m+1 <= p so we must show A[p-1] < x: o if m+1 < p, then A[p-1] < x (from (2)); o if p <= m+1, then p-1 <= m so A[p-1] <= A[m] < x (A is sorted); in all cases, A[p-1] < x; . if p <= e, then x <= A[p] (from (3)). Therefore, current call satisfies postcondition. In all cases, current call satisfies postcondition. Therefore, by induction, RecBinSearch is correct. - NOTES: This may seem complicated, but only because we thought through borderline cases carefully -- in a sense, we ensured code works in all cases from the start, rather than write code carelessly and waste time fixing it up afterwards. - MergeSort(A,b,e): 1. if b == e: return 2. m = (b + e) / 2 3. MergeSort(A,b,m) 4. MergeSort(A,m+1,e) # merge sorted A[b..m] and A[m+1..e] back into A[b..e] 5. for i in [b..e]: B[i] = A[i] 6. c = b 7. d = m+1 8. for i in [b..e]: 9. if d > e or (c <= m and B[c] < B[d]): 10. A[i] = B[c] 11. c += 1 else: # d <= e and (c > m or B[c] >= B[d]) 12. A[i] = B[d] 13. d += 1 Precondition? 1 <= b <= e <= length(A) elements of A[b..e] comparable with each other Postcondition? MergeSort(A,b,e) terminates and A[b..e] contains same elements as before, but sorted in non-decreasing order (A[b] <= ... <= A[e]) Proof of correctness: By induction on size n = e-b+1, prove (precondition and execution) implies postcondition. Inductive structure of proof will follow recursive structure of algorithm. Base case: n = 1, i.e., e = b. Then, algo terminates and returns A unchanged, which satisfies postcondition. Ind. Hyp.: Let k > 1 and suppose postcondition holds after execution for all inputs of size j that satisfy precondition, for 1 <= j < k. Ind. Step: Consider call MergeSort(A,b,e) when e-b+1 = k >= 2. Test on line 1 fails, so b < e (since b <= e by precondition and b =/= e by negation of test) and algo executes line 2. Since b <= floor((b+e)/2) < e, IH implies that MergeSort(A,b,m) terminates and A'[b..m] contains same elements as A[b..m] sorted in non-decreasing order. For the same reason, MergeSort(A,m+1,e) terminates and A'[m+1..e] contains same elements as A[m+1..e] sorted in non-decreasing order. (Notation: to compare elements of A at various points during execution, use A' to refer to order after execution of recursive calls, and A'' to refer to order after execution of current call.) Line 5 copies A'[b..e] into B[b..e] (obvious enough to state without proof). Lines 6-13 merge B[b..m] and B[m+1..e] into A''[b..e], which satisfies postcondition -- this requires formal proof, but we lack tools to carry this out for now; will come back to it later. -------------------- Iterative Algorithms -------------------- - Example: algorithm to compute x^y def pow(x, y): z = 1 m = 0 while m < y: z *= x m += 1 return z Precondition? x in R, y in N Postcondition? pow(x, y) returns x^y (with convention 0^0 = 1) - To deal with loop, insert comments with what we know (or want to prove) at various points: def pow(x, y): # function precondition: x in R, y in N z = 1 m = 0 # loop precondition: x in R, y in N, z = 1, m = 0 while m < y: z *= x m += 1 # loop postcondition: m = y, z = x^y return z # function postcondition: returns x^y - To describe values of variables during different iterations, use notation v_k = value of v at the end of k complete iterations (just before loop test evaluated for (k+1)st time). Conventions: v_{k+1} = v_k if there is no iteration number k+1; subscript omitted for variables that do not change during loop. - In our example, suppose y = 2, then i | 0 1 2 3 ... ----|-------------------- m_i | 0 1 2 2 ... z_i | 1 x x^2 x^2 ... Termination: - Standard method: find expression E involving program variables such that: . E_k in N for all k; . for all k, E_{k+1} < E_k if loop iterates at least k+1 times (E_{k+1} = E_k otherwise). - By well-ordering, sequence E_0 > E_1 > ... must be finite, i.e., the loop terminates. - Example: What quantity gets smaller? m gets larger, closer to y, so let E = y-m. Then, E_0 = y - m_0 = y in N, and if iteration k+1 is performed, E_{k+1} = y - m_{k+1} = y - (m_k + 1) = y - m_k - 1 = E_k - 1 < E_k. Also, iteration k+1 is performed when m_k < y so E_k = y - m_k > 0 so E_{k+1} = E_k - 1 >= 0. Hence, E satisfies both conditions, and loop terminates.