Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
COM S 311 SPRING 2022 HOMEWORK 2 SAMPLE SOLUTIONS (1) Solve the following recurrence equations: Assume that T (n) is constant for sufficiently small n. (a) T (n) = 5T (n/4) + log n Answer. Apply the Master Theorem. We have a = 5, b = 4, and f (n) = log n. Thus, nlogb a = nlog4 5 ≈ n1.161 and f (n) = log n = O(nlog4 5− ) for any < log4 5. Hence, Case 1 of the Master Theorem applies, and T (n) = Θ(nlog4 5 ). (b) T (n) = 16T (n/2) + n4 . Answer. Apply the Master Theorem. We have a = 16, b = 2, and f (n) = n4 . Thus, nlogb a = nlog2 16 = n4 and f (n) = Θ(n4 ). Hence, Case 2 of the Master Theorem applies, and T (n) = Θ(n4 log n). (c) T (n) = 9T (n/3) + n2 log n Answer. We first try to apply the Master Theorem. We have a = 9, b = 3, and f (n) = n2 log n. Thus, nlogb a = nlog3 9 = n2 . Now, • n2 log n is not O(n2− ), so Case 1 does not apply, • n2 log n is not Θ(n2 ), so Case 2 does not apply, and • n2 log n is not Ω(n2+ ), so Case 3 does not apply. Thus, instead of the Master Theorem, we use the recursion tree method to bound T (n). The recursion tree is shown below. n 2 log n n2 n log (3) 9 9 2 2 log3 n n2 n log (3) 9 n n n n ⋯ log log (3) (9) 81 81 9 ⋯ 9 ⋯ ⋯ 9 n2 n n n log ⋯ log (3) 81 (3) 81 2 ⋯ 9 n 2 log n 9 ⋯ 9 n2 n log (3) 9 n2 n log (9) ⋯ 81 ⋯ ≤ n 2 log n 9 n2 n log (9) 81 9 ⋯ ≤ n 2 log n 9 ⋮ 1 1 1 ⋯ 1 9 log3 n =n log3 9 = n leaves 2 1 1 1 n2 Total ≤ (n 2 log n) ⋅ log3 n + n 2 From the recursion tree, we see that log3 n−1 T (n) = X i=0 log3 n−1 = X 9i n 2 3i n2 log i=0 2 log n 3i n 3i + 9log3 n + nlog3 9 ≤ (n log n) · log n + n2 = O(n(log n)2 ). Note that it is possible to show that this bound is tight — i.e., T (n) = Θ(n2 (log n)2 ). Students are not required to prove this. In the next problems, assume that array indices start at 1. (2) The algorithm below can be proved to correctly (although inefficiently) sort and array A. You do not need to prove that this algorithm is correct. 1 2 3 4 5 6 7 8 9 10 SillySort(A, i, j) Input: An array A of n integers, and two indices, i and j, such that 1 ≤ i ≤ j ≤ n. Result: Subarray A[i . . j] is sorted in nondecreasing order . n=j−i+1 // The size of the subarray we are sorting if n == 2 then if A[i] > A[j] then Swap A[i] and A[j] else if n > 2 then m = bn/3c SillySort(A, i, j − m) // Sort the first part SillySort(A, i + m, j) // Sort the last part SillySort(A, i, j − m) // Sort the first part again (a) Write a recurrence equation for the running time T (n) of SillySort. Answer. The recurrence is ( Θ(1) if n ≤ 2 T (n) = 3T (2n/3) + c if n > 2. Note that we assume that only a reference to the subarrays of A is passed to the recursive calls. If the subarrays have to be copied, the c in the recurrence becomes cn. (b) What is the solution to the recurrence of part (a)? Answer. Apply the Master Theorem. We have a = 3, b = 3/2, and f (n) = c. Thus, nlogb a = nlog3/2 3 ≈ n2.71 and f (n) = O(nlog3/2 3− ), for any ≤ log3/2 3. Hence, Case 1 of the Master Theorem applies, and T (n) = O(n2.71 ). Note that the solution for T (n) remains the same if c is replaced by cn in the recurrence of part (a). (c) Write the recurrence equation for the running time T (n) of SillySort assuming that we replace Line 7 with m = bn/4c. 2 Answer. The recurrence is ( Θ(1) if n ≤ 2 T (n) = 3T (3n/4) + c if n > 2. Note that we assume that only a reference to the subarrays of A is passed to the recursive calls. If the subarrays have to be copied, the c in the recurrence becomes cn. (d) What is the solution to the recurrence of part (c)? Answer. Apply the Master Theorem. We have a = 3, b = 4/3, and f (n) = c. Thus, nlogb a = nlog4/3 3 ≈ n3.82 and f (n) = O(nlog4/3 3− ), for any ≤ log4/3 3. Hence, Case 1 of the Master Theorem applies, and T (n) = O(n3.82 ). Note that the solution for T (n) remains the same if c is replaced by cn in the recurrence of (3) Design a divide-and-conquer algorithm for the following problem. The input is an array A consisting of n distinct integers, which can be positive, negative, or zero. Assume that A is sorted from low to high. The algorithm should determine if there is an index i such that A[i] = i. Make your algorithm as fast as you can possibly make it. For full credit, you must • give pseudocode for your algorithm, • briefly justify the correctness of your algorithm, and • analyze the running time of your algorithm. Answer. The pseudocode for our algorithm is shown below. 1 2 3 4 5 6 7 8 9 10 FindIndex(A, left, right) Input: An array A of n distinct integers in increasing order, and two indices left and right into A. The elements of A may be positive or nonpositive Output: true if there is an index i ∈ {left, left + 1, . . . , right} such that A[i] == i; false if no such i exists. if left > right then return false mid = b(left + right)/2c if mid == A[mid] then return true if mid < A[mid] then return FindIndex(A, left, mid − 1) else return FindIndex(A, mid + 1, right) Note that if left > right, it must be the case that there is no i ∈ {left, left + 1, . . . , right}, so the algorithm correctly returns false. Correctness follows directly from the following property, which we shall argue holds at each call to FindIndex: Property. If there exists an index i ∈ {left, left + 1, . . . , right} such that A[i] = i, then, either 1. i = mid and line 6 returns the correct result, 2. i < A[mid], and i ∈ {left, left + 1, . . . , mid − 1}, or 3. i > A[mid] and i ∈ {mid + 1, mid + 2, . . . , right}. 3 Proof. 1. If mid = A[mid], then we clearly have i = mid. 2. If mid < A[mid], then, since the elements A are strictly increasing, j < A[j] for all j ∈ {mid + 1, mid + 2, . . . , right}, so if there exists an index i ∈ {left, left + 1, . . . , right} such that A[i] = i, it must be the case that i ∈ {left, left + 1, . . . , mid − 1}. 3. If mid > A[mid], then, since the elements A are strictly increasing, j > A[j] for all j ∈ {left, left + 1, . . . , mid − 1}, so so if there exists an index i ∈ {left, left + 1, . . . , right} such that A[i] = i, it must be the case that i ∈ {mid + 1, mid + 2, . . . , right}. The running time T (n) of a call to FindIndex(A, 1, n) satisfies the same recurrence as binary search. That is, ( Θ(1) if n = 1 T (n) = T (n/2) + c if n > 1. Thus, T (n) = Θ(log n). (4) Suppose you have an n-element array A of intervals (xi , yi ), where xi , yi are integers such that xi ≤ yi . The interval (xi , yi ) represents the set of integers between xi and yi . For example, the interval (3, 6) represents the set {3, 4, 5, 6}. Define the overlap of two intervals I, I 0 to be the number of integers that are members of both intervals. For example, the overlap of intervals I = (3, 6) and I 0 = (5, 8) is 2, since there are only two integers, namely 5 and 6, that are in both I and I 0 . The goal of this problem is to design a divide-and-conquer algorithm that takes as input an n-element array A of intervals and returns the highest overlap among all pairs of intervals in A. Assume that A is sorted by the xi -values of its intervals. (a) A naı̈ve approach is to compute the overlap between every pair of intervals. What is the exact number of comparisons that this approach needs? What is the running time of this approach? Answer. The number is n2 = n(n − 1)/2, leading to a Θ(n2 )-time algorithm. (b) Consider the following potential divide-and-conquer algorithm for the problem. First, divide A into two parts L and R of length n/2, such that all the xi -values of intervals in L are smaller than the xi -values of intervals in R. Next, recursively determine the maximum overlap pL in L and the maximum overlap pR in R. Finally, return max{pL , pR }. Why is this not enough to give the correct answer? Answer. This approach fails to take into account overlaps between an interval in L and an interval in R. (c) Taking advantage of the fact that the array A is sorted, design a divide-and-conquer algorithm that is faster than the naı̈ve approach. For full credit, you must • give pseudocode for your algorithm and • briefly justify the correctness of your algorithm. Answer. For an interval I, we write I.x to denote the left endpoint of I and I.y to denote the right endpoint of I. Thus, I = (I.x, I.y). We assume that we have a function Overlap such that, for any two intervals I and I 0 , Overlap(I, I 0 ) returns the overlap of I and I 0 in Θ(1) time. 4 Algorithm MaxOverlap, shown below follows the divide-and-conquer idea outlined in part (b), recursively computing pL and pR for the two sublists L and R, but additionally invokes a procedure CrossOverlap that counts the number, pRL of overlaps between an interval in L and an interval in R. 1 2 3 4 5 6 7 8 9 MaxOverlap(A) Input: An array A of n intervals sorted by nondecreasing left endpoint. Output: The maximum overlap between any pair of intervals in A. if n == 1 then return 0 L = [A[1], . . . , A[bn/2c]] R = [A[bn/2c + 1], . . . , A[n]] pL = MaxOverlap(L) pR = MaxOverlap(R) pLR = CrossOverlap(L, R) return max{pL , pR , pLR } Assuming CrossOverlap, described next, is correct, MaxOverlap is also correct. We argue this inductively. Base case: When n = 1, there are no overlaps, and MaxOverlap(A) correctly returns 0. Induction hypothesis: MaxOverlap returns the correct answer for any arrays with up to n − 1 intervals. Induction step: Suppose A is an array of n intervals. Then, every overlap in A is either (1) between two intervals un L, (2) between two intervals in R, or (3) between an interval in L and an interval in R. Since L and R have at most n − 1 intervals, MaxOverlap correctly computes the maximum overlaps pL and pR for each subarray. Thus, assuming CrossOverlap is correct, MaxOverlap correctly returns the maximum overlap in A. Algorithm CrossOverlap is shown below. 1 2 3 4 5 6 7 8 9 10 11 12 CrossOverlap(L, R) Input: Arrays L and R of nL and nR intervals, respectively. L and R are sorted by nondecreasing left endpoints, and L[nL ].x ≤ R[1].x. Output: The maximum overlap between an interval in L and an interval in R. rightmost = 1 for i = 2 to nL do if L[i].y > L[rightmost].y then rightmost = i if L[rightmost].y < R[1].x then return 0 curMax = 0 for i = 1 to nR do if Overlap(L[rightmost], R[i]) > curMax then curMax = Overlap(L[rightmost], R[i]) return curMax Lines 2–5 search for the interval L[rightmost] in L whose right endpoint is maximum. If L[rightmost].y < R[1].x, then every interval in L is to the left of any interval in R, so CrossOverlap correctly returns 0 in Line 7. Otherwise, the maximum overlap between an interval in L and an interval in R must be between L[rightmost] and some 5 interval in R. Thus, Lines 9–11 successively examine each interval in R to find the one that has the maximum overlap with L[rightmost]. The maximum overlap found is returned in Line 12. (d) What is the recurrence for the running time of your divide-and-conquer algorithm in part (c)? Answer. The base case takes constant time. Otherwise, there are two recursive calls on inputs of size n/2 and the combination (merge) work in CrossOverlap is linear. This leads to the following recurrence: ( Θ(1) if n = 1 T (n) = 2T (n/2) + cn if n > 1. (e) What is the running time of the algorithm in part (c)? Answer. The recurrence of part (d) is the same as that for merge sort. Its solution, therefore, is T (n) = Θ(n log n) 6