0% found this document useful (0 votes)
90 views

Tutorial1 Solutions

The document provides solutions to three questions related to analyzing algorithms using recurrence relations and the master theorem. Question 1 asks to find the asymptotic upper bound for four different recurrence relations using the master theorem. Question 2 asks for a O(log n) time algorithm to find the first index where a monotonically decreasing function becomes non-positive. Question 3 asks for a divide-and-conquer algorithm to find the maximum subarray sum in O(n) time by having the recursive calls return additional information.

Uploaded by

john doe
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
90 views

Tutorial1 Solutions

The document provides solutions to three questions related to analyzing algorithms using recurrence relations and the master theorem. Question 1 asks to find the asymptotic upper bound for four different recurrence relations using the master theorem. Question 2 asks for a O(log n) time algorithm to find the first index where a monotonically decreasing function becomes non-positive. Question 3 asks for a divide-and-conquer algorithm to find the maximum subarray sum in O(n) time by having the recursive calls return additional information.

Uploaded by

john doe
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 4

CSC373 Fall’21

Tutorial 1 Solutions
Monday, Sep 20, 2021

Master Theorem (General Version):


For constants a > 1 and b > 1, and an asymptotically positive function f (n), the recurrence relation
T (n) 6 a · T (n/b) + O(f (n)) has the following solution.

1. If f (n) = O nlogb a− for some constant  > 0, then T (n) = O nlogb a .
 

2. If f (n) = Θ nlogb a logk n for some constant k > 0, then T (n) = O nlogb a logk+1 n .
 

3. If f (n) = Ω nlogb a+ for some constant  > 0 and f satisfies the regularity condition*, then


T (n) = O (f (n)).
(*Regularity condition: For some constant c < 1 and all sufficiently large n, a·f (n/b) 6 c·f (n).)

Note: There are recurrence relations which do not fall under any of these three cases (e.g. the
recurrence relation T (n) 6 T (n/5) + T (7n/10) + O(n) from QuickSelect where the smaller instances
√ √
are not of uniform size, or the recurrence relation T (n) 6 n · T ( n) + O(n) where a and b are
not constants). If you’re interested in how more general recurrences can be solved, there are some
excellent resources available online.12

Q1 Practicing Recurrence Relations


Find the best possible asymptotic upper bound for T (n) under the following recurrence relations.3
(a) T (n) 6 3 · T (n/2) + O(n log3 n)
(b) T (n) 6 4 · T (n/2) + O(n2 )
(c) T (n) 6 2 · T (n/2) + O(n log2 n)
(d) T (n) 6 2 · T (n/4) + O(n0.5001 )

Solution to Q1

(a) For T (n) 6 3T (n/2) + O(n log3 n), we have:

ˆ a = 3 and b = 2; thus, nlogb a = nlog2 3 .

ˆ f (n) = n log3 n.

Hence, by case 1 of the Master theorem, T (n) = O(nlog2 3 ).

1
http://jeffe.cs.illinois.edu/teaching/algorithms/notes/99-recurrences.pdf
2
http://web.csulb.edu/~tebert/teaching/lectures/528/recurrence/recurrence.pdf
3
Note that when proving an upper bound on the worst-case running time of an algorithm, you would encounter
equations of the form T (n) 6 . . . rather than T (n) = . . ., yielding T (n) = O(·) rather than T (n) = Θ(·). To derive a
lower bound, you need to explicitly construct instances on which the algorithm takes at least the claimed amount of
time.

1
(b) For T (n) 6 4T (n/2) + O(n2 ), we have:

ˆ a = 4 and b = 2; thus, nlogb a = nlog2 4 = n2 .

ˆ f (n) = n2 .

Hence, by case 2 of the Master theorem, T (n) = O(n2 log n).

(c) For T (n) 6 2T (n/2) + O(n log2 n), we have

ˆ a = 2 and b = 2; thus, nlogb a = nlog2 2 = n.

ˆ f (n) = n log2 n.

Hence, again by case 2 of the Master theorem, T (n) = O(n log3 n).

(d) For T (n) 6 2T (n/4) + O(n0.5001 ), we have

ˆ a = 2 and b = 4; thus, nlogb a = nlog4 2 = n0.5 .

ˆ f (n) = n0.5001 .

Hence, by case 3 of the Master theorem, T (n) = O(n0.5001 ).

Q2 Monotonic Function Evaluation


Consider a monotonously decreasing function f : N → Z (that is, a function defined on natural
numbers which takes integer values and satisfies f (i) > f (i + 1) for all i ∈ N). Assuming we can
evaluate f at any point i in constant time, we want to find n = min{i ∈ N |f (i) 6 0} (that is, we
want to find the first point where f becomes non-positive). Note that n is not given to us, but
we are told that some point i with f (i) 6 0 exists (i.e. n is well-defined), and we are allowed to
express the running time of our algorithm in terms of n.
We can obviously solve the problem in O(n) time by simply evaluating f (1), f (2), f (3), . . . , f (n).
Describe an O(log n) time algorithm.
[Hint: Try to quickly get an estimate of n, and then precisely pinpoint the exact value of n in the
range you estimated.]

Solution to Q2
Let k = 1, and while f (k) > 0, double k. In at most dlog ne iterations, this will terminate as we
will have k > n. Let k ∗ be the value at which it terminates. Then, we know that k ∗ /2 < n 6 k ∗ .
We can binary-search n in this range (or for simplicity, in the range 1 . . . k ∗ ), as described by the
function FindFirstNonPositive below.
The running time for finding k ∗ is O(log n), and the running time for the subsequent binary search
is also O(log n). Hence, the overall running time is O(log n).

2
1 Function FindFirstNonPositive(A[1 . . . r]):
2 if r = 1 then
3 return A[1]

4 m ← br/2c
5 if A[m] 6 0 then
6 return FindFirstNonPositive(A[1 . . . m])
7 else
8 return FindFirstNonPositive(A[(m + 1) . . . r])

Q3 Maximum Subarray Sum


You are given an array P
A[1 . . . n], and you are asked to find the maximum subarray sum, that is,
the maximum value of jt=i A[t] over all possible (i, j) with 1 6 i 6 j 6 n. Design an O(n) time
divide and conquer algorithm for the problem.
[Hint: Once you divide the array into two equal halves, say A[1 . . . mid] and A[mid +1 . . . n], you
will get the maximum subarray sum within each half. What extra information do you need from
each half?
If you spend O(n) time in the merge step to calculate this extra information, you will get O(n log n)
running time. Can you get your recursive algorithm to return this information instead?]

Solution to Q3
Suppose we have already calculated the maximum subarray sums in the two halves, A[1 . . . mid] and
A[mid +1 . . . n]. To find the overall maximum subarray sum, we need a third value: the maximum
subarray sum where the subarray overlaps both halves.
Note that this quantity is the sum of two quantities: the maximum suffix sum in the left half (i.e.
maximum sum of any A[i . . . mid]) and the maximum prefix sum in the right half (i.e. maximum
sum of any A[mid +1 . . . j]).
After recursively calling our algorithm on the two halves, we could spend O(n) time calculating
the maximum suffix sum in the left half and the maximum prefix sum in the right half. But as the
hint suggests, this will give us T (n) = 2 · T (n/2) + O(n), i.e., T (n) = O(n log n).
Instead, we want our recursive algorithm to return the maximum suffix sum in addition to the max-
imum subarray sum, when called on the left half, and return the maximum prefix sum in addition
to the maximum subarray sum, when called on the right half.
That means, our algorithm must return three quantities: maximum subarray sum, maximum prefix
sum, and maximum suffix sum. Note that the “parent” call must also return these quantities once
getting them from the two recursive calls, otherwise it is not a legitimate recursive algorithm.
When thinking along these lines, it becomes clear that to compute the maximum prefix and suffix

3
sums in the entire array, we will also need to know the sum of the left and right halves. Also, in
prefix and suffix sums, we need to allow empty prefix and suffix, which plays a key role in the first
step of the algorithm below.

1 Function Subarray-Prefix-Suffix-Sum(A[` . . . r]):


2 if r = ` then
3 return (A[`], max(A[`], 0), max(A[`], 0), A[`])

4 mid ← b(` + r)/2c


5 [subarrayLeft,prefixLeft,suffixLeft,sumLeft] ← Subarray-Prefix-Suffix-Sum(A[` . . . mid])
6 [subarrayRight,prefixRight,suffixRight,sumRight] ←
Subarray-Prefix-Suffix-Sum(A[mid +1 . . . r])
7 subarray ← max(subarrayLeft,subarrayRight,suffixLeft+prefixRight)
8 prefix ← max(prefixLeft,sumLeft+prefixRight)
9 suffix ← max(suffixRight,suffixLeft+sumRight)
10 sum ← sumLeft+sumRight
11 return [subarray,prefix,suffix,sum]

Note that the worst-case running time of this algorithm is given by T (n) 6 2 · T (n/2) + O(1), which
yields T (n) = O(n).

You might also like