CMSC 451: Lecture 10 Dynamic Programming: Weighted Interval Scheduling Tuesday, Oct 3, 2017

Similar documents
memoization or iteration over subproblems the direct iterative algorithm a basic outline of dynamic programming

CMSC 451: Dynamic Programming

Dynamic Programming. Nothing to do with dynamic and nothing to do with programming.

Introduction to Algorithms / Algorithms I Lecturer: Michael Dinitz Topic: Dynamic Programming I Date: 10/6/16

Algorithm Design and Analysis

Algorithmic Paradigms

Chapter 6. Dynamic Programming. Modified from slides by Kevin Wayne. Copyright 2005 Pearson-Addison Wesley. All rights reserved.

Chapter 6. Dynamic Programming

CMSC 451: Lecture 11 Dynamic Programming: Longest Common Subsequence Thursday, Oct 5, 2017

Dynamic Programming. Introduction, Weighted Interval Scheduling, Knapsack. Tyler Moore. Lecture 15/16

Design and Analysis of Algorithms Prof. Madhavan Mukund Chennai Mathematical Institute. Module 02 Lecture - 45 Memoization

CS 473: Fundamental Algorithms, Spring Dynamic Programming. Sariel (UIUC) CS473 1 Spring / 42. Part I. Longest Increasing Subsequence

Lectures 12 and 13 Dynamic programming: weighted interval scheduling

6.001 Notes: Section 4.1

Dynamic Programming. Applications. Applications. Applications. Algorithm Design 6.1, 6.2, 6.3

We augment RBTs to support operations on dynamic sets of intervals A closed interval is an ordered pair of real

1 Non greedy algorithms (which we should have covered

Algorithms for Data Science

Subset sum problem and dynamic programming

CSC 373 Lecture # 3 Instructor: Milad Eftekhar

CS 380 ALGORITHM DESIGN AND ANALYSIS

Elements of Dynamic Programming. COSC 3101A - Design and Analysis of Algorithms 8. Discovering Optimal Substructure. Optimal Substructure - Examples

Greedy Algorithms 1 {K(S) K(S) C} For large values of d, brute force search is not feasible because there are 2 d {1,..., d}.

5.1 The String reconstruction problem

Memoization/Dynamic Programming. The String reconstruction problem. CS124 Lecture 11 Spring 2018

Outline. CS38 Introduction to Algorithms. Fast Fourier Transform (FFT) Fast Fourier Transform (FFT) Fast Fourier Transform (FFT)

Greedy Algorithms 1. For large values of d, brute force search is not feasible because there are 2 d

Algorithm classification

Dynamic Programming Part One

Algorithm Design Techniques part I

1 More on the Bellman-Ford Algorithm

Scribe: Virginia Williams, Sam Kim (2016), Mary Wootters (2017) Date: May 22, 2017

Unit-5 Dynamic Programming 2016

Input: n jobs (associated start time s j, finish time f j, and value v j ) for j = 1 to n M[j] = empty M[0] = 0. M-Compute-Opt(n)

Greedy algorithms is another useful way for solving optimization problems.

COMP 182: Algorithmic Thinking Dynamic Programming

CSC 505, Spring 2005 Week 6 Lectures page 1 of 9

Introduction to Algorithms

The Knapsack Problem an Introduction to Dynamic Programming. Slides based on Kevin Wayne / Pearson-Addison Wesley

Greedy Algorithms CLRS Laura Toma, csci2200, Bowdoin College

Framework for Design of Dynamic Programming Algorithms

Dynamic Programming. Lecture Overview Introduction

Applied Algorithm Design Lecture 3

15.4 Longest common subsequence

Ensures that no such path is more than twice as long as any other, so that the tree is approximately balanced

Design and Analysis of Algorithms

IN101: Algorithmic techniques Vladimir-Alexandru Paun ENSTA ParisTech

1 Format. 2 Topics Covered. 2.1 Minimal Spanning Trees. 2.2 Union Find. 2.3 Greedy. CS 124 Quiz 2 Review 3/25/18

Greedy Algorithms CHAPTER 16

Efficient Sequential Algorithms, Comp309. Problems. Part 1: Algorithmic Paradigms

Algorithms Dr. Haim Levkowitz

10/24/ Rotations. 2. // s left subtree s right subtree 3. if // link s parent to elseif == else 11. // put x on s left

Analysis of Algorithms. Unit 4 - Analysis of well known Algorithms

10. EXTENDING TRACTABILITY

Lecture 22: Dynamic Programming

CS473-Algorithms I. Lecture 11. Greedy Algorithms. Cevdet Aykanat - Bilkent University Computer Engineering Department

Lecture 2. 1 Introduction. 2 The Set Cover Problem. COMPSCI 632: Approximation Algorithms August 30, 2017

Special Topics on Algorithms Fall 2017 Dynamic Programming. Vangelis Markakis, Ioannis Milis and George Zois

Multithreaded Algorithms Part 1. Dept. of Computer Science & Eng University of Moratuwa

CMPS 2200 Fall Dynamic Programming. Carola Wenk. Slides courtesy of Charles Leiserson with changes and additions by Carola Wenk

Theorem 2.9: nearest addition algorithm

ESO207A: Data Structures and Algorithms End-semester exam

Computer Sciences Department 1

Last week: Breadth-First Search

Lecture 12: Dynamic Programming Part 1 10:00 AM, Feb 21, 2018

Introduction to Algorithms / Algorithms I Lecturer: Michael Dinitz Topic: Shortest Paths Date: 10/13/15

Algorithmic Paradigms. Chapter 6 Dynamic Programming. Steps in Dynamic Programming. Dynamic Programming. Dynamic Programming Applications

1 i n (p i + r n i ) (Note that by allowing i to be n, we handle the case where the rod is not cut at all.)

Algorithms. Ch.15 Dynamic Programming

MergeSort, Recurrences, Asymptotic Analysis Scribe: Michael P. Kim Date: April 1, 2015

Name: Lirong TAN 1. (15 pts) (a) Define what is a shortest s-t path in a weighted, connected graph G.

Today: Matrix Subarray (Divide & Conquer) Intro to Dynamic Programming (Rod cutting) COSC 581, Algorithms January 21, 2014

12 Dynamic Programming (2) Matrix-chain Multiplication Segmented Least Squares

TU/e Algorithms (2IL15) Lecture 2. Algorithms (2IL15) Lecture 2 THE GREEDY METHOD

Solving NP-hard Problems on Special Instances

CMSC Introduction to Algorithms Spring 2012 Lecture 16

2.3 Algorithms Using Map-Reduce

Dynamic Programming Assembly-Line Scheduling. Greedy Algorithms

So far... Finished looking at lower bounds and linear sorts.

Subsequence Definition. CS 461, Lecture 8. Today s Outline. Example. Assume given sequence X = x 1, x 2,..., x m. Jared Saia University of New Mexico

Towards a Memory-Efficient Knapsack DP Algorithm

Advanced Algorithms and Data Structures

Chapter 3 Dynamic programming

Dynamic Programming Intro

Divide and Conquer Algorithms

1. (a) O(log n) algorithm for finding the logical AND of n bits with n processors

Algorithms for Integer Programming

CMSC 754 Computational Geometry 1

COMP 352 FALL Tutorial 10

CS 506, Sect 002 Homework 5 Dr. David Nassimi Foundations of CS Due: Week 11, Mon. Apr. 7 Spring 2014

CS 6783 (Applied Algorithms) Lecture 5

1 Dynamic Programming

We will give examples for each of the following commonly used algorithm design techniques:

CSE 521: Algorithms. Dynamic Programming, I Intro: Fibonacci & Stamps. W. L. Ruzzo

COMP 250 Fall recurrences 2 Oct. 13, 2017

The divide-and-conquer paradigm involves three steps at each level of the recursion: Divide the problem into a number of subproblems.

CS60020: Foundations of Algorithm Design and Machine Learning. Sourangshu Bhattacharya

Approximation Algorithms

Week - 03 Lecture - 18 Recursion. For the last lecture of this week, we will look at recursive functions. (Refer Slide Time: 00:05)

DEPARTMENT OF COMPUTER SCIENCE AND ENGINEERING QUESTION BANK UNIT-III. SUB NAME: DESIGN AND ANALYSIS OF ALGORITHMS SEM/YEAR: III/ II PART A (2 Marks)

Transcription:

CMSC 45 CMSC 45: Lecture Dynamic Programming: Weighted Interval Scheduling Tuesday, Oct, Reading: Section. in KT. Dynamic Programming: In this lecture we begin our coverage of an important algorithm design technique, called dynamic programming (or DP for short). The technique is among the most powerful for designing algorithms for optimization problems. Dynamic programming is a powerful technique for solving optimization problems that have certain well-defined clean structural properties. (The meaning of this will become clearer once we have seen a few examples.) There is a superficial resemblance to divide-and-conquer, in the sense that it breaks problems down into smaller subproblems, which can be solved recursively. However, unlike divideand-conquer problems, in which the subproblems are disjoint, in dynamic programming the subproblems typically overlap each other, and this renders straightforward recursive solutions inefficient. Dynamic programming solutions rely on two important structural qualities, optimal substructure and overlapping subproblems. Optimal substructure: This property (sometimes called the principle of optimality) states that for the global problem to be solved optimally, each subproblem should be solved optimally. While this might seem intuitively obvious, not all optimization problems satisfy this property. For example, it may be advantageous to solve one subproblem suboptimally in order to conserve resources so that another, more critical, subproblem can be solved optimally. Overlapping Subproblems: While it may be possible subdivide a problem into subproblems in exponentially many different ways, these subproblems overlap each other in such a way that the number of distinct subproblems is reasonably small, ideally polynomial in the input size. An important issue is how to generate the solutions to these subproblems. There are two complementary (but essentially equivalent) ways of viewing how a solution is constructed: Top-Down: A solution to a DP problem is expressed recursively. This approach applies recursion directly to solve the problem. However, due to the overlapping nature of the subproblems, the same recursive call is often made many times. An approach, called memoization, records the results of recursive calls, so that subsequent calls to a previously solved subproblem are handled by table look-up. Bottom-up: Although the problem is formulated recursively, the solution is built iteratively by combining the solutions to small subproblems to obtain the solution to larger subproblems. The results are stored in a table. In the next few lectures, we will consider a number of examples, which will help make these concepts more concrete. Lecture Fall

CMSC 45 Weighted Interval Scheduling: Let us consider a variant of a problem that we have seen before, the Interval Scheduling Problem. Recall that in the original (unweighted) version we are given a set S = {,..., n of n activity requests, where each activity is expressed as an interval [s i, f i ] from a given start time s i to a given finish time f i. The objective is to compute any maximum sized subset of non-overlapping intervals (see Fig. (a)). optimal unweighted optimal weighted opt = opt = 4 8 (a) (b) Fig. : Weighted and unweighted interval scheduling. In weighted interval scheduling, we assume that in addition to the start and finish times, each request is associated with a numeric weight or value, call it v i, and the objective is to find a set of non-overlapping requests such that sum of values of the scheduled requests is maximum (see Fig. (b)). The unweighted version can be thought of as a special case in which all weights are equal to. Although a greedy approach works fine for the unweighted problem, no greedy solution is known for the weighted version. We will demonstrate a method based on dynamic programming. Recursive Formulation: Dynamic programming solutions are based on a decomposition of a problem into smaller subproblems. Let us consider how to do this for the weighted interval scheduling problem. As we did in the greedy algorithm, it will be convenient to sort the requests in nondecreasing order of finish time, so that f... f n. Here is the idea behind DP in a nutshell. Consider the last request [s n, f n ]. There are two possibilities. If this request is not in the optimum schedule, then we can safely ignore it, and recursively compute the optimum solution of the first n requests. Otherwise, this request is in the optimal solution. We will schedule this request (receiving the profit of v n ) and then we must eliminate all the requests whose intervals overlap this one. Because requests have been sorted by finish time, this involves finding the largest index p such that f p < s n. Thus, we solve the problem recursively on the first p requests. But we don t know the optimum solution, so how can we select among these two options? The answer is that we will compute the cost of both of them recursively, and take the better of the two. Let s now implement this idea. Recall that the requests are sorted by finish times. For the sake of generality, let s assume that we want to solve the problem on requests through j, where j n. If j =, there is nothing to do. Otherwise, given any request j, define p(j) to be the largest integer such that f p(j) < s j. Thus, request through p(j) do not overlap request j. If no such i exists, let p(j) = (see Fig. ). Lecture Fall

CMSC 45 j intervals and values p(j).5 4 5 8. Fig. : Weighted interval scheduling input and p-values. For now, let s just concentrate on computing the optimum total value. Later we will consider how to determine which requests produce this value. A natural idea would be to define a function that gives the optimum value for just the first i requests. Definition: For j n, opt(j) denotes the maximum possible value achievable if we consider just tasks {,..., j (assuming that tasks are given in order of finish time). As a basis case, observe that if we have no tasks, then we have no value. Therefore, opt() =. If we can compute opt(j) for each value of j, then clearly, the final desired result will be the maximum value using all the requests, that is, opt(n). Summarizing our earlier observations, in order to compute opt(j) for an arbitrary j, we observe that there are two possibilities: Request j is not in the optimal schedule: If j is not included in the schedule, then we should do the best we can with the remaining j requests. Therefore, opt(j) = opt(j ). Request j is in the optimal schedule: If we add request j to the schedule, then we gain v j units of value, but we are now limited as to which other requests we can take. We cannot take any of the requests following p(j). Thus we have opt(j) = v j + opt(p(j)). How do we know which of the these two options to select? The danger in trying to be too smart (e.g., trying a greedy choice) is that the choice may not be correct. Instead, the best approach is often simple brute force: DP Selection Principle: When given a set of feasible options to choose from, try them all and take the best. In this cases there are two options, which suggests the following recursive rule: { opt(j ) (request j is not in opt) opt(j) = max. v j + opt(p(j)) (request j is in opt) We could express this in pseudocode as shown in the following code block: Lecture Fall

CMSC 45 Recursive Weighted Interval Scheduling recursive-wis(j) { if (j == ) return else return max( recursive-wis(j-), v[j] + recursive-wis(p[j]) ) I have left it as self-evident that this simple recursive procedure is correct. Indeed the only subtlety is the inductive observation that, in order to compute opt(j) optimally, the two subproblems that are used to make the final result opt(j ) and opt(p(j)) should also be computed optimally. This is an example of the principle of optimality, which in this case is clear. Memoized Formulation: The principal problem with this elegant and simple recursive procedure is that it has a horrendous running time. To make this concrete, let us suppose that p(j) = j for all j. Let T (j) be the number of recursive function calls to opt() that result from a single call to opt(j). Clearly, T () =, T () = T ()+T (), and for j, T (j) = T (j )+T (j ). If you start expanding this recurrence, you will find that the resulting series is essentially a Fibonacci series: j 4 5 8... 5 T (j) 5 8 4 55...,,8,9,95,8,99 It is well known that the Fibonacci series F (j) grows exponentially as a function of j. This may seem ludicrous. (And it is!) Why should it take billion recursive calls to fill in a table with just 5 entries? If you look at the recursion tree, the reason jumps out immediately (see Fig ). The problem is that the same recursive calls are being generated over and over again. But there is no reason to make even a second call this, since they all return exactly the same value. opt(5) opt() opt(4) opt() opt() opt() opt() opt() opt() opt() opt() opt() opt() opt() opt() opt() opt() opt() opt() opt() opt() opt() opt() opt() opt() Fig. : The exponential nature of recursive-wis. Why would you want to solve a subproblem suboptimally? Suppose, that you had the additional constraint that the final schedule can only contain intervals. Now, it might be advantageous to solve a subproblem suboptimally, so that you have a few extra intervals left over to use at a later time. The nth Fibonacci number is roughly grows as Θ(φ n ), where φ.8 is the celebrated Golden Ratio. Lecture 4 Fall

CMSC 45 This suggests a smarter version of the algorithm. Once a value has been computed for recursive-wis(j) we store the value in a global array M[..n], and all future attempts to compute this value will simply access the array, rather than making a recursive call. This technique is called memoizing (I guess because we are making a memo to ourselves of what the value is for future reference). You might imagine that we initialize all the M[j] entries to initially, and use this special value to determine whether an entry has already been computed. We will add one additional piece of information, which will help in reconstructing the final schedule. Whenever a choice is made between two options, we ll save a predecessor pointer, pred[j], which reminds of which choice we made. (Its value is either j or p(j), depending on which recursive call was made). The resulting algorithm is presented in the following code block. Memoized Weighted Interval Scheduling memoized-wis(j) { if (j == ) return // basis case - no requests else if (M[j] has been computed) return M[j] else { leaveweight = memoized-wis(j-) // total weight if we leave j takeweight = v[j] + memoized-wis(p[j]) // total weight if we take j if ( leaveweight > takeweight ) { M[j] = leaveweight // better to leave j pred[j] = j- // previous is j- else { M[j] = takeweight // better to take j pred[j] = p[j] // previous is p[j] return M[j] // return final weight The memoized version runs in O(n) time. To see this, observe that each invocation of memoized-wis either returns in O() time (with no recursive calls), or it computes one new entry of M. Since there are n entries in the table, the latter can occur at most n times. Bottom-up Construction: (Optional) Yet another method for computing the values of the array, is to dispense with the recursion altogether, and simply fill the table up, one entry at a time. We need to be careful that this is done in such an order that each time we access the array, the entry being accessed is already defined. This is easy here, because we can just compute values in increasing order of j. As before, we include the computation of the predecessor values. Do you think that you understand the algorithm now? If so, answer the following question. Would the algorithm be correct if, rather than sorting the requests by finish time, we had instead sorted them by start time? How about if we didn t sort them at all? Computing the Final Schedule: So far we have seen how to compute the value of the optimal schedule, but how do we compute the schedule itself? This is a common problem that arises Lecture 5 Fall

CMSC 45 bottom-up-wis() { M[] = for (i = to n) { leaveweight = M[j-] takeweight = v[j] + M[p[j]] if ( leaveweight > takeweight ) { M[j] = leaveweight else { Bottom-Up Weighted Interval Scheduling // total weight if we leave j // total weight if we take j // better to leave j pred[j] = j- // previous is j- M[j] = takeweight pred[j] = p[j] // better to take j // previous is p[j] in many DP problems, since most DP formulations focus on computing the numeric optimal value, without consideration of the object that gives rise to this value. The solution is to leave ourselves a few hints in order to reconstruct the final result. j 4 5.5 8. v:.5 8. p: M: 4 5 4 4 4 (a) (b) Fig. 4: (a) The input intervals and p values, (b) the bottom-up construction of the table and predecessor values, (c) the final predecessor values. In bottom-up-wis() we did exactly this. We know that value of M[j] arose from two distinct possibilities, either () we didn t take j and just used the result of M[j ], or () we did take j, added its value v j, and used M[p(j)] to complete the result. To remind us of how we obtained the best choice for M[j] was to store a predecessor pointer pred[j]. In order to generate the final schedule, we start with M[n] and work backwards. In general, if we arrive at M[j], we check whether pred[j] = p[j]. If so, we can surmise that we did used the jth request, and we continue with pred[j] = p[j]. If not, then we know that we did not include request j in the schedule, and we then follow the predecessor link to continue with pred[j] = j. The algorithm for generating the schedule is given in the code block below. The computation of the final schedule is illustrated in Fig. 5 (where predecessor values are shown as arrows). Lecture Fall

CMSC 45 Computing Weighted Interval Scheduling Schedule get-schedule() { j = n // start with the last request sched = empty while (j > ) { if (pred[j] == p[j]) { // take request j prepend j to the front of sched j = pred[j] // continue with j s predecessor return sched // return the final schedule Since pred[] = 5 p[], we do not use request, and we continued with pred[] = 5. Since pred[5] = = p[5], we use request 5, and we continue with pred[5] =. Since pred[] = p[], we do not use request, and we continued with pred[] =. Since pred[] = = p[], we use request, and we continue with pred[] =, and terminate. We obtain the final list 5,. 4 5 p: M: 4 5 4 4 Take Take 5 Fig. 5: Following the predecessor links to compute the final schedule. Lecture Fall