Lecture 1 – (Chapter 2) An Introduction to Algorithmic Problem-Solving Techniques Mark Fienup (fienup@cs.uni.edu) Fall 2007 Many Problem-solving Techniques Greedy Divide-and-Conquer Dynamic Programming Backtracking Branch-and-Bound (Best-First Search) Machine learning Randomized Greedy Approach Idea: use "local" information to make what looks like current “best” choice without regard for past or future choices Most intuitive problem-solving technique Example: Coin-change Problem Goal: make change with the fewest number of coins assuming unlimited supply of each type of coin Making 29-cents change with coins {1, 5, 10, 25, 50} What's the natural greedy algorithm? Example 1: Coin-Change Problem Given the set of US coin types {1, 5, 10, 25, 50} and an amount of change to be returned (say 29-cents), determine the fewest number of coins for this amount of change. 0) What intuitive algorithm would you use to solve this problem? Greedy Coin-change Algorithm Greedy selection-criteria: return largest coin that is less than or equal to the remaining change Problem: Making 29-cents change with coins {1, 5, 10, 25, 50} 29 - 25 4 -1 3 -1 2 -1 1 -1 0 Return the largest coin less than or equal to 29 cents, i.e., 25-cent coin Return the largest coin less than or equal to 4 cents, i.e., 1-cent coin Return the largest coin less than or equal to 3 cents, i.e., 1-cent coin Return the largest coin less than or equal to 2 cents, i.e., 1-cent coin Return the largest coin less than or equal to 1 cents, i.e., 1-cent coin A 5-coin solution Does this greedy algorithm always give the fewest number of coins? Problem with Greedy Approach Does not give the fewest coins for all coin sets. Problem: Making 29-cents change with coins {1, 5, 10, 12, 25, 50} 29 - 25 4 -1 3 -1 2 -1 1 -1 0 Return the largest coin less than or equal to 29 cents, i.e., 25-cent coin Return the largest coin less than or equal to 4 cents, i.e., 1-cent coin Return the largest coin less than or equal to 3 cents, i.e., 1-cent coin Return the largest coin less than or equal to 2 cents, i.e., 1-cent coin Return the largest coin less than or equal to 1 cents, i.e., 1-cent coin This 5-coin solution is NOT optimal! What solution would require the fewest coins? Divide-and-Conquer Idea: Solve problem by: dividing it into small problem(s) solving the smaller problem(s) use solution(s) to smaller problem(s) to solve original problem Consider the coin-change problem. What determines the size of the problem? How would we divide the coin-change problem for 29-cents into smaller problems? If we knew the solution to these smaller problems, how would we be able to solve the orginal problem? Divide-and-Conquer Coin-change Solution After we give back the first coin, then we have a smaller amount of change. Original Problem 24 cents 10cen t co 5025 cen -ce t co nt in co in oin 28 cents in t co t coin n e c 1en 5-c c ent Possible First Coin c 12- in 29 cents 19 cents 17 cents 4 cents (not feasible) -21 cents Smaller problems If we knew the fewest number of coins needed for each possible smaller problem, then how could determine the fewest number of coins needed for the original problem? n coi ent 10c ent in oin t co n c e c t 1en 5-c c 12- Possible First Coin coi n 29 cents 5025 c en -ce t co nt in co in 28 cents 24 cents 19 cents 17 cents 4 cents 4-coin solution (1+5+10+12) 2-coin solution (12+12) 4-coin solution (1+1+5+12) 2-coin solution (5+12) 4-coin solution (1+1+1+1) (not feasible) -21 cents We can write a recursive relationship for the fewest number of coins as: minimum( FewestCoins(change - coin ) ) + 1 if change FewestCoins(change) = coin CoinSet CoinSet 1 if change CoinSet A C++ function to recursively find the fewest number of coins: int FewestCoins(int change, int coinTypes[], int numberOfCoinTypes) { int coin, minNumberOfCoins, numberOfCoins; // Find the min. number of coins after giving back one coin of each type minNumberOfCoins = INT_MAX; for (coin = 0; coin < numberOfCoinTypes; coin++){ if (change == coinTypes[coin]) { return 1; } else if (change > coinTypes[coin]) { numberOfCoins = FewestCoins(change-coinTypes[coin], coinTypes, numberOfCoinTypes); if (numberOfCoins < minNumberOfCoins) { minNumberOfCoins = numberOfCoins; } // end if (numberOfCoins... } // end if (change... } // end for (coin... return minNumberOfCoins + 1; } // end FewestCoins Problem with Divide-and-Conquer Solution 29 1 5 28 1 5 10 10 12 24 12 25 27 23 18 16 1 3 5 25 19 10 12 23 19 14 12 1 17 10 12 5 18 14 9 7 1 5 16 12 4 10 12 7 5 1 3 A lot of redundant calculations!!! For coins of {1, 5, 10, 12, 25, 50}, typical timings: Change Amount 50 55 60 65 70 Run-Time (seconds) 1 5 26 126 613 Dynamic Programming Idea: Solve smaller problems before larger ones store their answers look-up answers to smaller problems when solving larger subproblems Advantage: Eliminates redundant work since each smaller problem solved only once! 7). In a dynamic programming coin-change solution, you fill an array fewestCoins from 0 to the amount of change. An element of fewestCoins stores the fewest number of coins necessary for the amount of change corresponding to its index value. For 29-cents using the set of coin types {1, 5, 10, 12, 25, 50}, determine the value of fewestCoins[0] to fewestCoins[7]. 0 1 2 3 4 5 6 7 29 fewestCoins: 8) For 29-cents using the set of coin types {1, 5, 10, 12, 25, 50}, determine the value of fewestCoins[29]. Previously Calculated fewestCoins: 4 4 17 2 25 19 4 12 24 2 10 28 29 4 5 1 Dynamic Programming Coin-change Solution Fills an array fewestCoins from 0 to the amount of change An element of fewestCoins stores the fewest number of coins necessary for the amount of change corresponding to its index value. For 29-cents using the set of coin types {1, 5, 10, 12, 25, 50}: Previously Calculated fewestCoins: 4 4 17 2 25 19 4 12 24 2 10 28 29 4 3 5 1 fewestCoins[29] = minimum(fewestCoins[28], fewestCoins[24], fewestCoins[19], fewestCoins[17], fewestCoins[4] ) + 1 = 2 + 1 = 3 NOTICE: that the algorithm has previously calculated the fewestCoins for the change amounts of 0, 1, 2, ..., up to 28 cents