Divide and Conquer Algorithms: Tutorial Questions and Solutions, Exercises of Algorithms and Programming

[Week 6] Master Theorem, Recurrence Relations

Typology: Exercises

2018/2019

Uploaded on 04/20/2019

kefart
kefart 🇺🇸

4.4

(11)

55 documents

1 / 5

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
Tutorial questionsAlgorithms 9007
2019 Semester 1Tutorial 5
The University of Sydney
School of Computer Science
Pre-tutorial questions
Do you know the basic concepts of this week’s lecture content? These questions are only to test yourself.
They will not be explicitly discussed in the tutorial, and no solutions will be given to them.
1. Divide and Conquer
(a) What is the general idea of a Divide-and-Conquer algorithm? Can you break up the general
structure into three steps?
(b) Why are recursions usually needed to analyse the running time of a Divide-and-Conquer algo-
rithm?
2. Algorithms
(a) Describe the general idea of merge sort.
(b) What are the three main steps in the algorithm for counting inversions?
3. Recurrences
(a) State the master method.
(b) Try to explain the master method using a recursion tree.
Tutorial
Problem 1
Solve the following recurrences:
1. T(n) = 4T(n/2) + n2
2. T(n) = T(n/2) + 2n
3. T(n) = 16T(n/4) + n
4. T(n) = 2T(n/2) + nlog n
5. T(n) = 2T(n/2) + logn
6. T(n) = 3T(n/2) + n
7. T(n) = 3T(n/3) + n
1
pf3
pf4
pf5

Partial preview of the text

Download Divide and Conquer Algorithms: Tutorial Questions and Solutions and more Exercises Algorithms and Programming in PDF only on Docsity!

Algorithms 9007 Tutorial questions 2019 Semester 1 Tutorial 5

The University of Sydney School of Computer Science

Pre-tutorial questions

Do you know the basic concepts of this week's lecture content? These questions are only to test yourself. They will not be explicitly discussed in the tutorial, and no solutions will be given to them.

  1. Divide and Conquer

(a) What is the general idea of a Divide-and-Conquer algorithm? Can you break up the general structure into three steps? (b) Why are recursions usually needed to analyse the running time of a Divide-and-Conquer algo- rithm?

  1. Algorithms

(a) Describe the general idea of merge sort. (b) What are the three main steps in the algorithm for counting inversions?

  1. Recurrences

(a) State the master method. (b) Try to explain the master method using a recursion tree.

Tutorial

Problem 1 Solve the following recurrences:

  1. T (n) = 4T (n/2) + n^2
  2. T (n) = T (n/2) + 2n
  3. T (n) = 16T (n/4) + n
  4. T (n) = 2T (n/2) + n log n
  5. T (n) =

p 2 T (n/2) + logn

  1. T (n) = 3T (n/2) + n
  2. T (n) = 3T (n/3) +

p n

Solution: First we state the Master Theorem. The Master Theorem applies to recurrences of the following form: T (n) = aT (n/b) + f (n) where a  1 and b > 1 are constants and f (n) is an asymptotically positive function. There are three cases:

  1. If f (n) = O(nlogb^ a−ε) for some constant ε > 0, then T (n) = (nlogb^ a).
  2. If f (n) = (nlogb^ a^ logk^ n) with k  0, then T (n) = (nlogb^ a^ logk+1^ n).
  3. If f (n) = Ω(nlogb^ a+ε) with ε > 0, and f (n) satis es the regularity condition, then T (n) = (f (n)). Regularity condition: af (n/b)  cf (n) for some constant c < 1 and all sufficiently large n.
  4. T (n) = 4T (n/2) + n^2! T (n) = (n^2 log n) (Case 2)
  5. T (n) = T (n/2) + 2n^! (2n) (Case 3)
  6. T (n) = 16T (n/4) + n! T (n) = (n^2 ) (Case 1)
  7. T (n) = 2T (n/2) + n log n! T (n) = (n log^2 n) (Case 2)
  8. T (n) =

p 2 T (n/2) + logn! T (n) = (

p n) (Case 1)

  1. T (n) = 3T (n/2) + n! T (n) = (nlg 3) (Case 1)
  2. T (n) = 3T (n/3) +

p n! T (n) = (n) (Case 1)

Problem 2 Consider the following algorithm.

Algorithm 1 Reverse 1: function reverse(A) 2: if jAj = 1 then 3: return A 4: else 5: Let B and C be the rst and second half of A 6: return concatenate reverse(C) and reverse(B) 7: end if 8: end function

Let T (n) be the running time of the algorithm on a instance of size n. Write down the recurrence relation for T (n) and solve it by unrolling it.

Solution: T (n) = 2  T

( (^) n 2

  • O(n) = O(n log n)

Problem 3 The product of two n  n matrices X and Y is a third n  n matrix Z = XY , where the (i, j) entry of Z is Zij =

∑n k=1 XikYkj^. Suppose that^ X^ and^ Y^ are divided into four^ n/^2 ^ n/2 blocks each:

X =

[

A B

C D

]

and Y =

[

E F

G H

]

Solution:

  1. Let T (n) be the running time of the algorithm on an array of length n. Then T (n) = 3 T ( 23 n )+O(1), which is O

n log 323 )

. That's roughly O(n^2.^71 ). Much worse that bubble sort! Alex is heartbroken...

  1. The algorithm, however, is correct. Think of the bottom 13 of the elements in sorted order. After Line 6 is executed we are guaranteed that these element lie in the rst (^23) of the array and thus Line 7 correctly places them in the rst 13 of the array. A similar argument can be made about the top 13 of the elements in sorted order ending in the 1 3 of the array. Therefore, the middle^

1 3 of the elements in sorted order are placed in the middle 13 of the array. Within each 13 of the array the elements are sorted, so the array is indeed sorted at the end of the algorithm.

Problem 5 Given an array A holding n objects, we want to test whether there is a majority element; that is, we want to know whether there is an object that appears in more than n/2 positions of A. Assume we can test equality of two objects in O(1) time, but we cannot use a dictionary indexed by the objects. Your task is to design an O(n log n) time algorithm for solving the majority problem.

  1. Show that if x is a majority element in the array then x is a majority element in the rst half of the array or the second half of the array
  2. Show how to check in O(n) time if a candidate element x is indeed a majority element.
  3. Put these observation together to design a divide an conquer algorithm whose running time obeys the recurrence T (n) = 2T (n/2) + O(n)
  4. Solve the recurrence by unrolling it.

Solution:

  1. If x is a majority element in the original array then, by the pigeon-hole principle, x must be a majority element in at least one of the two halves. Suppose that n is even. If x is a majority element at least n/2 + 1 entries equal x. By the pigeon hole principle either the rst half or the second half must contain n/4 + 1 copies of x, which makes x a majority element within that half.

  2. We scan the array counting how many entries equal x. If the count is more than n/ 2 we declare x to be a majority element. The algorithm scans the array and spends O(1) time per element, so O(n) time overall.

  3. We break the input array A into two halves, recursively call the algorithm on each half, and then test in A if either of the elements returned by the recursive calls is indeed a majority element of A. If that is the case we return the majority element, otherwise, we report that there is \no majority element". To argue the correctness, we see if there is no majority element of A then the algorithm must return \no majority element" since no matter what the recursive call return, we always check if an element is indeed a majority element. Otherwise, if there is a majority element x in A we are guaranteed that one of our recursive call will identify x and the subsequent check will lead the algorithm to return x. Regarding time complexity, breaking the main problem into the two subproblems and testing the two candidate majority elements can be done in O(n) time. Thus we get the following recurrence T (n) = 2T (n/2) + O(n).

T (n) = O(n log n).

Problem 6 Suppose we are given an array A with n distinct numbers. We say an index i is locally optimal if A[i] < A[i1] and A[i] < A[i + 1] for 0 < i < n 1, or A[i] < A[i + 1] for if i = 0, or A[i] < A[i 1] for i = n 1. Design an algorithm for nding a locally optimal index using divide an conquer. Your algorithm should run in O(log n) time.

Solution: First we test whether i = 1 or i = n are locally optimal entries. Otherwise, we know that A[1] > A[2] and A[n 1] < A[n]. If n  4, it is easy to see that either i = 2 or i = 3 is locally optimal and we can check that in O(1) time. Otherwise, pick the middle positions in the array for example i = ⌈n/ 2 ⌉ and test whether i is locally optimal. If it is we are done, other wise A[i 1] < A[i] or A[i] > A[i + 1]; in the former case we recurse on A[1,... , i] in the latter we recurse on A[i,... , n]. Either way, we reduce the size of the array by half. Rather than creating a new array each time we recurse (which would take O(n) time) we can keep the working subarray implicitly by maintaining a pair of indices (b, e) telling us that we are working with the array A[b,... , e]. Thus, each call demands O(1) work plus the work done by recursive calls. This leads to the following recurrence,

T (n) = T (n/2) + O(1),

which solves to T (n) = O(log n).