300 likes | 318 Vues
Algorithms: Design and Analysis. 240-310 , Semester 2, 2018-2019. Objective introduce Dynamic Programming ( DP) look at several examples: Fibonacci, Knapsack compare DP to Greedy using Knapsack. 6. Dynamic Programming. 1. DP Features.
E N D
Algorithms: Design and Analysis 240-310, Semester2, 2018-2019 • Objective • introduce Dynamic Programming (DP) • look at several examples: Fibonacci, Knapsack • compare DP to Greedy using Knapsack 6. Dynamic Programming
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 • fib0 = 0 • fib1 = 1 • 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[] 0 0 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]; } } • Running time is linear = O(n) • Requires extra space for the fibs[] table = O(n) 1 1 2 0 3 0 :: :: 0 MAX
Speed-up 10th Fib: 55 Number of fib() calls: 19 10th Fib: 55 Number of fib() calls: 1 11th Fib: 89 Number of fib() calls: 3 11th Fib: 89 Number of fib() calls: 1
Bottom-up Fib F(k-1) F(k-1) F(k-2) temp prev curr Running time = O(n) Space requirement is 5 variables = O(1) ! 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)
3. 0-1 Knapsack Problem 28 22 18 1 6 2 6 5 1 7 item 3 item 1 item 4 item 0 item 2 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
Maximize cost with at most 3 items: w = w = 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 22 18 6 1 c = c = 0 0 1 1 6 6 7 7 7 7 18 18 22 19 24 24 28 25 25 29 25 29 40 25 1 6 5 2 item 0 item 3 item 2 item 1 Try to add item 3:
Mathematically • Items 0, 1, ... n have weights w0, w1, ...wnand costs c0, c1, ...cn. All values are positive integers. • W is the maximum capcity of the knapsack. • Definem[i,w] to be the maximum total cost with weight ≤w using items 0 to i.
Define m[i,w] recursively • 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. the maximum total cost with weight ≤ w using items 0 to i don't use wi use wi
Why use Dynamic Prog.? 22 18 18 1 6 5 6 5 1 2 item 3 item 2 item 2 item 1 item 0 c == 25; w == 8 c == 40; w == 11 earlier selection current selection The current selection may change an earlier selection:
Code NOT EXAMINABLE see Knapsack0l.java 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 11) 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()
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
4. 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 algorithmsnever change choices made earlier in the calculation • makes "greedy" code easier to implement than DP
Examples: • Minimum Spanning Tree Algorithms – Kruskal’s and Prim’s • last year • Dijkstra’s Algorithm • in part 10
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 22 28 18 1 6 uses a greedy algorithm 2 1 5 7 6 item 3 item 1 item 4 item 0 item 2 divisible; can use parts of a weight maximize cost, but total weight ≤ 11 11 Crucial idea: order by cost per unit weight
28 22 18 18 22 28 1 6 6 1 5 2 7 2 6 5 6 7 1 1 item 2 item 0 item 3 item 1 item 2 item 1 item 4 item 0 item 4 item 3 reorder by decreeasing cost/unit weight: cost/unit weight: 4 3.666 3.6 3 1
Maximize cost by adding weights (or parts) in decreasing cost/ unit weight: 22 28 6 7 7 + 4 Max weight == 11 : item 3 item 4 Max cost == 7 * 4 + 4 * 3.666 = 28 + 14.666 = 42.666
Input Data Format • 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? 28 28 22 7 7 6 item 4 item 3 item 4 7 * 4 7 * 4 + 4 * 3.666 c = 28; w == 7 c == 42.666; w == 11 earlier selection current selection The current selection does not affect the earlier selection: