Algorithm Design and Analysis (ADA)

Slides:



Advertisements
Similar presentations
Lecture 8: Dynamic Programming Shang-Hua Teng. Longest Common Subsequence Biologists need to measure how similar strands of DNA are to determine how closely.
Advertisements

CPSC 335 Dynamic Programming Dr. Marina Gavrilova Computer Science University of Calgary Canada.
Overview What is Dynamic Programming? A Sequence of 4 Steps
Algorithms Dynamic programming Longest Common Subsequence.
COMP8620 Lecture 8 Dynamic Programming.
David Luebke 1 5/4/2015 CS 332: Algorithms Dynamic Programming Greedy Algorithms.
Lecture 7 Topics Dynamic Programming
Longest Common Subsequence
Dynamic Programming Introduction to Algorithms Dynamic Programming CSE 680 Prof. Roger Crawfis.
Dynamic Programming. Well known algorithm design techniques:. –Divide-and-conquer algorithms Another strategy for designing algorithms is dynamic programming.
ADA: 7. Dynamic Prog.1 Objective o introduce DP, its two hallmarks, and two major programming techniques o look at two examples: the fibonacci.
CSC401: Analysis of Algorithms CSC401 – Analysis of Algorithms Chapter Dynamic Programming Objectives: Present the Dynamic Programming paradigm.
DP (not Daniel Park's dance party). Dynamic programming Can speed up many problems. Basically, it's like magic. :D Overlapping subproblems o Number of.
Greedy Methods and Backtracking Dr. Marina Gavrilova Computer Science University of Calgary Canada.
6/4/ ITCS 6114 Dynamic programming Longest Common Subsequence.
CSC 221: Recursion. Recursion: Definition Function that solves a problem by relying on itself to compute the correct solution for a smaller version of.
Introduction to Algorithms Jiafen Liu Sept
Contest Algorithms January 2016 Introduce DP; Look at several examples; Compare DP to Greedy 11. Dynamic Programming (DP) 1Contest Algorithms: 11. DP.
1 Dynamic Programming Topic 07 Asst. Prof. Dr. Bunyarit Uyyanonvara IT Program, Image and Vision Computing Lab. School of Information and Computer Technology.
Dynamic Programming.  Decomposes a problem into a series of sub- problems  Builds up correct solutions to larger and larger sub- problems  Examples.
Fundamental Data Structures and Algorithms Ananda Guna March 18, 2003 Dynamic Programming Part 1.
9/27/10 A. Smith; based on slides by E. Demaine, C. Leiserson, S. Raskhodnikova, K. Wayne Adam Smith Algorithm Design and Analysis L ECTURE 16 Dynamic.
CS583 Lecture 12 Jana Kosecka Dynamic Programming Longest Common Subsequence Matrix Chain Multiplication Greedy Algorithms Many slides here are based on.
Dynamic Programming Typically applied to optimization problems
Merge Sort 5/28/2018 9:55 AM Dynamic Programming Dynamic Programming.
Fundamental Structures of Computer Science
Towers of Hanoi Move n (4) disks from pole A to pole C
David Meredith Dynamic programming David Meredith
CS 3343: Analysis of Algorithms
Seminar on Dynamic Programming.
CS200: Algorithm Analysis
Chapter 8 Dynamic Programming.
CSCE 411 Design and Analysis of Algorithms
Algorithm Design Methods
Algorithm design and Analysis
Dynamic Programming.
Dynamic Programming.
Dynamic Programming.
Prepared by Chen & Po-Chuan 2016/03/29
Data Structures and Algorithms
CS Algorithms Dynamic programming 0-1 Knapsack problem 12/5/2018.
Dynamic Programming Dr. Yingwu Zhu Chapter 15.
CS 3343: Analysis of Algorithms
Advanced Algorithms Analysis and Design
Dynamic Programming 1/15/2019 8:22 PM Dynamic Programming.
Merge Sort Dynamic Programming
Dynamic Programming Dynamic Programming 1/15/ :41 PM
CS6045: Advanced Algorithms
Dynamic Programming.
Dynamic Programming Dynamic Programming 1/18/ :45 AM
Dynamic Programming Merge Sort 1/18/ :45 AM Spring 2007
Longest Common Subsequence
Lecture 8. Paradigm #6 Dynamic Programming
Algorithms: Design and Analysis
Trevor Brown DC 2338, Office hour M3-4pm
Dynamic Programming-- Longest Common Subsequence
Introduction to Algorithms: Dynamic Programming
Dynamic Programming.
CSC 413/513- Intro to Algorithms
Algorithm Design Techniques Greedy Approach vs Dynamic Programming
Longest Common Subsequence
Merge Sort 4/28/ :13 AM Dynamic Programming Dynamic Programming.
Longest common subsequence (LCS)
Analysis of Algorithms CS 477/677
This is not an advertisement for the profession
Longest Common Subsequence
Dynamic Programming Merge Sort 5/23/2019 6:18 PM Spring 2008
Dynamic Programming.
Seminar on Dynamic Programming.
Algorithm Course Dr. Aref Rashad
Presentation transcript:

Algorithm Design and Analysis (ADA) 242-535, Semester 1 2017-2018 7. Dynamic Programming Objective introduce DP; look at several examples compare DP to Greedy

Overview 1. DP Features 2. The fibonacci series 3. Binomial coefs 4. 0-1 knapsack 5. Longest Common Subsequence 6. Compare DP to Greedy using fractional knapsack

1. DP Features An optimal (best) solution to the problem is a composition of optimal (best) subproblem solutions makes the code recursive (perhaps) The same subproblems appear many times while solving the problem use tabling / memoziation to 'remember' answers perhaps calculate subproblems first; called bottom-up evaluation The current best solution choice may change solutions choices made earlier.

2. Fibonacci Series Series defined by Recursive algorithm: fibn = fibn-1 + fibn-2 Recursive algorithm: Running Time? O(2n) 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, … see FibMemo.java public static int fib(int n) { if (n < 2) return n; else return fib(n-1) + fib(n-2); }

Fibonacci can be a DP problem: the solution (fib(n)) is a combination of sub-solutions fib(n-1) and fib(n-2)) There are many repeated subproblems 2n subproblems, but only n are different

Memoization + top-down fib() fibs[] private static long fibs = new long[MAX+1]; // in main() fibs[0] = 0; fibs[1] = 1; public static long fib(int n) { if (n < 2) return n; else { if (fibs[n] == 0) fibs[n] = fib(n−1) + fib(n−2) return fibs[n]; } } 1 1 2 3 : : : : MAX Running time is linear = O(n) Requires extra space for the fibs[] table = O(n)

Speed-up 10th Fib: 55 Number of fib() calls: 19 Number of fib() calls: 1 11th Fib: 89 Number of fib() calls: 3

Bottom-up Fib Running time = O(n) F(k-1) F(k-2) F(k-1) temp prev curr int fib(int n) { if (n == 0) return 0; else { // deal with 1, 1, 2,... int prev = 0; int curr = 1; int temp; for (int i=1; i < n; i++) { temp = prev + curr; prev = curr; curr = temp; } return curr; + temp prev curr F(k) F(k-1) F(k) Running time = O(n) Space requirement is 5 variables = O(1) !

3. Binomial Coefficients How many ways are there to choose k things out of n? Used to calculate the coefficients of of (a+b)n

Use Pascal’s triangle instead: It’s difficult to calculate the value of binomial coefficients using that equation: arithmetic overflow will happen for n > 12! Use Pascal’s triangle instead: n == 0 1 1 1 1 2 1 3 3 1 1 4 6 4 1 5 10 10 5 1 1 6 15 20 15 6 1 n == 1 : a table of subproblems that are used repeatedly. A DP problem start counting k = 0 from left

Binomial Coefficients see Binomial.java private static final int MAX = 100; // return n choose m static long binCoef(int n, int m) { long[][] bc = new long[MAX][MAX}; //table of binomial coefs for(int i = 0; i <= n; i++) bc[i][0] = 1; // init upper edges for(int j = 0; j <= n; j++) bc[j][j] = 1; for(int i = 1; i <= n; i++) // fill inside triangle for(int j = 1; j < i; j++) bc[i][j] = bc[i-1][j-1] + bc[i-1][j]; return bc[n][m]; }

4. 0-1 Knapsack Problem 11 1 item 0 2 6 item 1 5 18 item 2 6 22 item 3 7 28 item 4 indivisible; use or not use a weight maximize cost, but total weight ≤ 11 11 Crucial idea: total weight must be one of 12 values: 0-11

1 item 0 2 6 item 1 5 18 item 2 Maximize cost with at most 3 items: 1 2 3 4 5 6 7 8 9 10 11 w = c = 18 19 24 25 6 22 item 3 Try to add item 3: 1 2 3 4 5 6 7 8 9 10 11 w = c = 18 22 24 28 29 40

Mathematically Items 0, 1, ... n have weights w0, w1, ...wn and costs c0, c1, ...cn. All values are positive integers. W is the maximum capcity of the knapsack. Define m[i,w] to be the maximum total cost with weight ≤ w using items 0 to i.

Define m[i,w] recursively the maximum total cost with weight ≤ w using items 0 to i m[0,w] = c0 if w0 ≤ w m[i,w] = m[i-1,w], if wi > w  the i item weighs more than the current weight limit m[i,w] = max( m[i-1,w], m[i-1, w-wi]+ci ), if wi ≤ w The solution is m[n,W]. To do this efficiently we must use a table to store previous computations. don't use wi use wi

Why use Dynamic Prog.? The current selection may change an earlier selection: 1 item 0 2 6 item 1 5 18 item 2 5 18 item 2 6 22 item 3 c == 25; w == 8 c == 40; w == 11 earlier selection current selection

Code NOT EXAMINABLE see Knapsack0l.java m[][] has become totCosts[][] public static void main(String[] args) { int W = 11; // knapsack capacity System.out.println("Knapsack capacity: " + W); // costs and weights for items int[] ci = { 1, 6, 18, 22, 28}; int[] wi = { 1, 2, 5, 6, 7}; int numItems = ci.length; for (int i=0; i < numItems; i++) System.out.println("Item " + i + ": weight = " + wi[i] + ", cost = " + ci[i]); System.out.println(); // totCosts[i, w] stores the maximum total cost // of some items in {0,1,...,i} of combined weight <= w int[][] totCosts = new int[numItems][W + 1]; // used[i, weight] is true when item i is part of the solution for weight boolean[][] used = new boolean[numItems][W + 1]; // all false by default : m[][] has become totCosts[][]

// compute maximum cost for first item for (int w = 0; w <= W; w++) { if (wi[0] <= w) { totCosts[0][w] = ci[0]; // first line of maths (slide 15) used[0][w] = true; // means that item 0 can be used when weight is w } else totCosts[0][w] = 0; // compute maximum cost for rest of items for (int i = 1; i < numItems; i++) { for (int w = 0; w <= W; w++) { // w == current weight limit if (wi[i] <= w) { // item within current weight limit int costWith_i = ci[i] + totCosts[i-1][w-wi[i]]; if (costWith_i > totCosts[i-1][w]) { // higher cost is better; third line of maths totCosts[i][w] = costWith_i; used[i][w] = true; else // leave cost unchanged totCosts[i][w] = totCosts[i-1][w]; else // item exceeds current weight limit; don't use totCosts[i][w] = totCosts[i-1][w]; // second line of maths printTables(totCosts, used); itemsUsed(used, ci, wi); } // end of main()

private static void itemsUsed(boolean[][] used, int[] ci, int[] wi) { System.out.println("Items used:"); int wCapacity = used[0].length-1; // start at maximum weight (W) int usedWeight = 0; int usedCost = 0; // check if i is part of the set of items weighing wCapacity, // if yes, print i info, and reduce wCapacity by item i's weight // and find the next item for this new capacity for (int i = used.length-1; i >= 0; i--) { if (used[i][wCapacity]) { System.out.println("Item " + i + ": weight = " + wi[i] + ", cost = " + ci[i]); usedWeight += wi[i]; usedCost += ci[i]; wCapacity = wCapacity - wi[i]; } System.out.println("Total weight: " + usedWeight + "; Total cost: " + usedCost); } // end of itemsUsed()

Execution

Using used[][] Items used: Weight 0 1 2 3 4 5 6 7 8 9 10 11 Item 0: X X X X X X X X X X X Item 1: X X X X X X X X X X Item 2: X X X X X X X Item 3: X X X X X Item 4: X X X X W = 11 Item 3 used; weight == 6 W = 11 – 6 = 5 Item 2 used; weight == 5 W = 5 – 5 = 0 No item used; stop

5. Longest Common Subsequence (LCS) Given two sequences x[1 . . m] and y[1 . . n], find a longest subsequence common to them both. m values x: A B C B D A B BCBA = LCS(x, y) y: B D C A B A or BDAB, BCAB n values

Brute-force LCS Algorithm Check every subsequence of x[1 . . m] to see if it is also a subsequence of y[1 . . n]. Analysis • Checking time for each subsequence is O(n). • 2m subsequences of x[] (can use or not use each element in x). Worst-case running time = O(n*2m), exponential time.

Towards a Better LCS Algorithm Simplify the problem: Find the length of a LCS 2. We'll extend the algorithm later to find the LCS.

Prefixes If X = < A, B, C, B, D, A, B > then A prefix is x[1 .. 4] == < A, B, C, B > we abbreviate this as x4 Also x0 is the empty sequence

Creating a Table of Lengths c[] is a table (2D array) for storing LCS lengths: c[i, j] = | LCS(x[1. . i], y[1. . j]) | | s | is the length of a sequence s Since x is of length m, and y is of length n, then c[m, n] = | LCS(x, y) |

Calculating LCS Lengths Since X0 and Y0 are empty strings, their LCS is always empty (i.e. c[0, 0] == 0) The LCS of an empty string and any other string is empty, so for every i and j: c[0, j] == c[i, 0] == 0

Initial c[] 0 1 2 3 4 5 1 2 3 4 prefixes of Y n = prefixes of X m = 0 1 2 3 4 5 prefixes of X 1 2 3 m = 4 this will eventually contain the answer | LCS(x,y)| c[m, n] == c[3,2]

Recursive Definition of c[] The first line of this definition fills the top row and first column of c[] with 0's.

When we calculate c[i, j], there are two cases: First case: x[i] == y[j]: one more symbol in strings X and Y matches, so the length of LCS Xi and Yj equals the length of LCS of smaller strings Xi-1 and Yi-1 , plus 1

Second case: x[i] != y[j] As symbols don’t match, our solution is not improved, and the length of LCS(Xi , Yj) is the same as the biggest from before (i.e. max of LCS(Xi, Yj-1) and LCS(Xi-1,Yj)

Repeated Subproblems? Does the LCS() algorithm have many repeating (overlapping) subproblems? Consider the worst case execution x[i] ≠ y[ j], in which case the algorithm evaluates two subproblems, each with only one parameter decremented

Recursion Tree (in worst cases) Height = m + n. The total work is exponential, but we’re repeating lots of subproblems.

Memoization LCS(x, y, i, j) if c[i, j] is empty then // calculate if not already in c[i, j] if i == 0 or j == 0 then c[i, j] ← 0 else if x[i] == y[ j] then c[i, j] ← LCS(x, y, i–1, j–1) + 1 else c[i, j] ← max( LCS(x, y, i–1, j), LCS(x, y, i, j–1) ) return c[i, j] Time = O(m*n) == constant work per table entry Space = O(m*n)

Bottom-up Execution This algorithm works top-down start with large subsequences, and calculate the smaller subsequences Let's switch to bottom-up execution calculate the small subsequences first, then move to larger ones

LCS Length Bottom-up the same recursive definition of c[] as before LCS-Length(X, Y) 1. m = length(X) // get the # of symbols in X 2. n = length(Y) // get the # of symbols in Y 3. for i = 1 to m c[i,0] = 0 // special case: Y0 4. for j = 1 to n c[0,j] = 0 // special case: X0 5. for i = 1 to m // for all Xi 6. for j = 1 to n // for all Yj 7. if ( Xi == Yj ) 8. c[i,j] = c[i-1,j-1] + 1 9. else c[i,j] = max( c[i-1,j], c[i,j-1] ) 10. return c the same recursive definition of c[] as before

Bottom-up Examples LCS(X, Y) = BCB X = A B C B Y = B D C A B We’ll see how a bottom-up LCS works on: X = ABCB Y = BDCAB LCS(X, Y) = BCB X = A B C B Y = B D C A B LCS-length(X, Y) = 3

LCS Example 1 ABCB BDCAB X = ABCB; m = |X| = 4 Y = BDCAB; n = |Y| = 5 j 0 1 2 3 4 5 i Yj B D C A B Xi "" A 1 "A" B 2 "AB" 3 C "ABC" 4 B "ABCD" "" "B" "BD" "BDC" "BDCA" "BDCAB" X = ABCB; m = |X| = 4 Y = BDCAB; n = |Y| = 5 Allocate array c[5,4]

ABCB BDCAB for i = 1 to m c[i,0] = 0 for j = 1 to n c[0,j] = 0 Yj B D C A B Xi A 1 B 2 3 C 4 B for i = 1 to m c[i,0] = 0 for j = 1 to n c[0,j] = 0

ABCB BDCAB j 0 1 2 3 4 5 i Yj B D C A B Xi A 1 B 2 3 C 4 B A 1 B 2 3 C 4 B if ( Xi == Yj ) c[i,j] = c[i-1,j-1] + 1 else c[i,j] = max( c[i-1,j], c[i,j-1] )

ABCB BDCAB j 0 1 2 3 4 5 i Yj B D C A B Xi A 1 B 2 3 C 4 B A 1 B 2 3 C 4 B if ( Xi == Yj ) c[i,j] = c[i-1,j-1] + 1 else c[i,j] = max( c[i-1,j], c[i,j-1] )

ABCB BDCAB j 0 1 2 3 4 5 i Yj B D C A B Xi A 1 1 B 2 3 C 4 B A 1 1 B 2 3 C 4 B if ( Xi == Yj ) c[i,j] = c[i-1,j-1] + 1 else c[i,j] = max( c[i-1,j], c[i,j-1] )

ABCB BDCAB j 0 1 2 3 4 5 i Yj B D C A B Xi A 1 1 1 B 2 3 C 4 B A 1 1 1 B 2 3 C 4 B if ( Xi == Yj ) c[i,j] = c[i-1,j-1] + 1 else c[i,j] = max( c[i-1,j], c[i,j-1] )

ABCB BDCAB j 0 1 2 3 4 5 i Yj B D C A B Xi A 1 1 1 B 2 1 3 C 4 B A 1 1 1 B 2 1 3 C 4 B if ( Xi == Yj ) c[i,j] = c[i-1,j-1] + 1 else c[i,j] = max( c[i-1,j], c[i,j-1] )

ABCB BDCAB j 0 1 2 3 4 5 i Yj B D C A B Xi A 1 1 1 B 2 1 1 1 1 3 C 4 B A 1 1 1 B 2 1 1 1 1 3 C 4 B if ( Xi == Yj ) c[i,j] = c[i-1,j-1] + 1 else c[i,j] = max( c[i-1,j], c[i,j-1] )

ABCB BDCAB j 0 1 2 3 4 5 i Yj B D C A B Xi A 1 1 1 B 2 1 1 1 1 2 3 C 4 A 1 1 1 B 2 1 1 1 1 2 3 C 4 B if ( Xi == Yj ) c[i,j] = c[i-1,j-1] + 1 else c[i,j] = max( c[i-1,j], c[i,j-1] )

ABCB BDCAB j 0 1 2 3 4 5 i Yj B D C A B Xi A 1 1 1 B 2 1 1 1 1 2 3 C 1 A 1 1 1 B 2 1 1 1 1 2 3 C 1 1 4 B if ( Xi == Yj ) c[i,j] = c[i-1,j-1] + 1 else c[i,j] = max( c[i-1,j], c[i,j-1] )

ABCB BDCAB j 0 1 2 3 4 5 i Yj B D C A B Xi A 1 1 1 B 2 1 1 1 1 2 3 C 1 A 1 1 1 B 2 1 1 1 1 2 3 C 1 1 2 4 B if ( Xi == Yj ) c[i,j] = c[i-1,j-1] + 1 else c[i,j] = max( c[i-1,j], c[i,j-1] )

ABCB BDCAB j 0 1 2 3 4 5 i Yj B D C A B Xi A 1 1 1 B 2 1 1 1 1 2 3 C 1 A 1 1 1 B 2 1 1 1 1 2 3 C 1 1 2 2 2 4 B if ( Xi == Yj ) c[i,j] = c[i-1,j-1] + 1 else c[i,j] = max( c[i-1,j], c[i,j-1] )

ABCB BDCAB j 0 1 2 3 4 5 i Yj B D C A B Xi A 1 1 1 B 2 1 1 1 1 2 3 C 1 A 1 1 1 B 2 1 1 1 1 2 3 C 1 1 2 2 2 4 B 1 if ( Xi == Yj ) c[i,j] = c[i-1,j-1] + 1 else c[i,j] = max( c[i-1,j], c[i,j-1] )

ABCB BDCAB j 0 1 2 3 4 5 i Yj B D C A B Xi A 1 1 1 B 2 1 1 1 1 2 3 C 1 A 1 1 1 B 2 1 1 1 1 2 3 C 1 1 2 2 2 4 B 1 1 2 2 if ( Xi == Yj ) c[i,j] = c[i-1,j-1] + 1 else c[i,j] = max( c[i-1,j], c[i,j-1] )

3 ABCB BDCAB j 0 1 2 3 4 5 i Yj B D C A B Xi A 1 1 1 B 2 1 1 1 1 2 3 C A 1 1 1 B 2 1 1 1 1 2 3 C 1 1 2 2 2 3 4 B 1 1 2 2 if ( Xi == Yj ) c[i,j] = c[i-1,j-1] + 1 else c[i,j] = max( c[i-1,j], c[i,j-1] )

Running Time The bottom-up LCS algorithm calculates the values of each entry of the array c[m, n] So what is the running time? O(m*n) Since each c[i, j] is calculated in constant time, and there are m*n elements in the array

Code NOT EXAMINABLE see LCS.java c[][] has become lengths[][] private static void lcs(String x, String y) { int[][] lengths = new int[x.length()+1][y.length()+1]; // filled with 0's by default for (int i = 1; i <= x.length(); i++) { for (int j = 1; j <= y.length(); j++) { if (x.charAt(i-1) == y.charAt(j-1)) // Java string index starts at 0, not 1 lengths[i][j] = lengths[i-1][j-1]+1; else lengths[i][j] = Math.max(lengths[i][j-1], lengths[i-1][j]); } System.out.println("LCS length: " + lengths[x.length()][y.length()]); System.out.println("LCS: " + getSubSeq(x, y, lengths)); } // end of lcs() c[][] has become lengths[][]

Finding the LCS We have found the length of LCS. We want to modify this algorithm to have it calculate LCS of X and Y Each c[i, j] depends on c[i-1, j] and c[i, j-1] or c[i-1, j-1] For each c[i, j] we can trace back how it was calculated: 2 2 For example, here c[i, j] = c[i-1, j-1] +1 = 2+1=3 2 3

Whenever c[i,j] = c[i-1, j-1]+1, record x[i] as part of the LCS Remember that: So we can start from c[m,n] (bottom right of c[]) and move left or up backwards if adj. squares are equal Whenever c[i,j] = c[i-1, j-1]+1, record x[i] as part of the LCS When i=0 or j=0, we have reached the beginning, so can stop.

Finding LCS Example 1 3 j 0 1 2 3 4 5 i Yj B D C A B Xi A 1 1 1 B 2 1 A 1 1 1 B 2 1 1 1 1 2 3 C 1 1 2 2 2 3 4 B 1 1 2 2

j 0 1 2 3 4 5 i Yj B D C A B Xi A 1 1 1 B 2 1 1 1 1 2 3 C 1 1 2 2 2 3 4 B 1 1 2 2

3 LCS: B C B j 0 1 2 3 4 5 i Yj B D C A B Xi A 1 1 1 B 2 1 1 1 1 2 3 C A 1 1 1 B 2 1 1 1 1 2 3 C 1 1 2 2 2 3 4 B 1 1 2 2 LCS: B C B

LCS() Offers Choices A B C B D A B 1 1 1 B 1 1 1 1 2 D 1 1 1 1 2 2 2 2 There may be several paths through the table, which represent different answers for LCS() LCS() = BDAB All of them have the LCS-length of 4 A B C B D A B 1 1 1 B 1 1 1 1 2 D 1 1 1 1 2 2 2 2 C 1 2 2 2 2 1 3 A 1 2 2 2 2 3 2 3 3 4 B 1 2 3 3 3 1 4 4 A 2 2 3 3 4 4

Code NOT EXAMINABLE see LCS.java c[][] has become lengths[][] private static String getSubSeq(String x, String y, int[][] lengths) { StringBuffer sb = new StringBuffer(); int i = x.length(); // start at bottom right int j = y.length(); while ((i != 0) && (j != 0)) { if (x.charAt(i-1) == y.charAt(j-1)) { // Java string index starts at 0, not 1 sb.append(x.charAt(i-1)); i--; j--; } else if (lengths[i][j-1] >= lengths[i][j]) j--; else i--; return sb.reverse().toString(); } // end of getSubSeq() c[][] has become lengths[][]

Execution

6. DP Compared to Greedy DP features Again Optimal sub-structure: the best solution to the problem uses the best solutions to sub-problems use recursive code Overlapping (repeating) problems: the same subproblems appear several times while solving the problem use tabling / memoziation to 'remember' answer The current best solution choice may change solutions choices made earlier.

Greedy Features Optimal sub-structure: (same as DP) the best solution to the problem uses the best solutions to sub-problems use recursive code The current best solution choice is made using only current ('local') information greedy algorithms never change choices made earlier in the calculation makes "greedy" code easier to implement than DP

Examples (we'll look at them later): Minimum Spanning Tree Algorithms – Kruskal’s and Prim’s Dijkstra’s Algorithm

Fractional Knapsack Problem Maximize the value of a knapsack that can hold at most W units worth of goods from a list of items I1, I2, ... In. Each item i has two attributes: Cost/unit == vi Weight == wi

Fractional Knapsack Algorithm Sort the items into a list by cost/unit. Take as much of the most expensive item as possible, then move down the list. You may end up taking a fractional portion of the last item.

Fractional Knapsack Problem 1 item 0 2 6 item 1 5 18 item 2 6 22 item 3 7 28 item 4 uses a greedy algorithm divisible; can use parts of a weight maximize cost, but total weight ≤ 11 11 Crucial idea: order by cost per unit weight

1 item 0 2 6 item 1 5 18 item 2 6 22 item 3 7 28 item 4 reorder by decreeasing cost/unit weight: 7 28 item 4 6 22 item 3 5 18 item 2 2 6 item 1 1 item 0 cost/unit weight: 4 3.666 3.6 3 1

Maximize cost by adding weights (or parts) in decreasing cost/ unit weight: 7 + 4 Max weight == 11 : 7 28 item 4 6 22 item 3 Max cost == 7 * 4 + 4 * 3.666 = 28 + 14.666 = 42.666

Input Data Format 5 11 1 1 6 2 18 5 22 6 see fkData.txt 28 7 No. of items, knapsack W Lines of item info; on each line: ci, wi for an item e.g. 5 11 1 1 6 2 18 5 22 6 28 7 see fkData.txt This is the example from the previous slides.

Code NOT EXAMINABLE public static void main(String[] args) throws Exception { if (args.length != 1) { System.out.println("Usage: java FracKnapsack <data-file>"); return; } Scanner sc = new Scanner(new File(args[0])); int numItems = sc.nextInt(); int maxW = sc.nextInt(); LinkedList<KItem> items = new LinkedList<KItem>(); for (int i = 0; i < numItems; i++) items.add( new KItem(sc.nextInt(), sc.nextInt()) ); Collections.sort(items); :

int currWeight = 0; double currCost = 0; while ((currWeight < maxW) && (!items.isEmpty())) { int remWeight = maxW - currWeight; KItem item = items.poll(); if (item.weight <= remWeight) { // add all of the item currWeight += item.weight; currCost += item.cost; } else { // item.weight > remWeight // add a fraction of the item currCost += remWeight * item.costWeightRatio; currWeight += remWeight; System.out.printf("%.3f", currCost); } // end of main()

public class KItem implements Comparable<KItem> { public int cost, weight; public double costWeightRatio; public KItem(int cost, int weight) this.cost = cost; this.weight = weight; costWeightRatio = ((double) cost) / weight; } public int compareTo(KItem i) double diff = costWeightRatio - i.costWeightRatio; if (diff > 0) return -1; else if (diff == 0) return 0; else return 1; } // end of compareTo() public String toString() { return "(cost: " + cost + ", weight: " + weight + ")"; } } // end of KItem class

Why is it Greedy? The current selection does not affect the earlier selection: 7 28 item 4 7 28 item 4 6 22 item 3 7 * 4 7 * 4 + 4 * 3.666 c = 28; w == 7 c == 42.666; w == 11 earlier selection current selection