Greedy algorithms are designed to find a solution by going one step at a time and using heuristics to determine the best choice. They are quick but not always lead to most optimum results since it might not take into consideration all the options to give a solution.
An excellent example of a greedy algorithm that doesn’t work well is finding the largest sum on a tree.
graph G { 5 -- 3 [color="#B8E986", penwidth=2] 5 -- 7 [color="#FF5252", penwidth=2] 3 -- 87 [color="#B8E986", penwidth=2] 3 -- 1 7 -- 2 7 -- 4 [color="#FF5252", penwidth=2] label="Optimal vs. Greedy path" }
Let’s say the greedy heuristics are set to take the more significant value. The greedy algorithm will start at the root and say "Which number is bigger 3 or 7?" Then go with 7 and later 4. As you can see in the diagram, the most significant sum would be the path 7 - 3 - 87
. A greedy algorithm never goes back on its options. This greedy choice makes it different from dynamic programming which exhaustive and it’s guaranteed to find the best option. However, when they work well, they are usually faster than other options.
Greedy algorithms are well suited when an optimal local solution is also a globally optimal solution.
Tip
|
Greedy algorithms make the choice that looks best at the moment based on a heuristic such as smallest, largest, best ratio, and so on. This algorithm only gives one shot at finding the solution and never goes back to consider other options. |
Don’t get the wrong idea; some greedy algorithms work very well if they are designed correctly.
-
part04-algorithmic-toolbox.asc: we select the best (minimum value) remove it from the input and then select the next minimum until everything is processed.
-
part04-algorithmic-toolbox.asc: the "merge" uses a greedy algorithm, where it combines two sorted arrays by looking at their current values and choosing the best (minimum) at every time.
-
Take a sample from the input data (usually in a data structure like array/list, tree, graph).
-
Greedy choice: use a heuristic function that will choose the best candidate. E.g., Largest/smallest number, best ratio, etc.
-
Reduce the processed input and repeat step #1 and #2 until all data is gone.
-
Return solution.
-
Check correctness with different examples and edge cases.
We are going to use the "Fractional Knapsack Problem" to learn how to design greedy algorithms. The problem is the following:
You are going to resell legumes (rice, beans, chickpeas, lentils) and you only brought a knapsack. What proportion of items can you choose to get the highest loot without exceeding the maximum weight of the bag?
Let’s say we have the following items available.
const items = [
{ value: 1, weight: 1},
{ value: 4, weight: 3 },
{ value: 5, weight: 4 },
{ value: 7, weight: 5 },
];
const maxWeight = 7;
So, we have four items that we can choose from. We can’t take them all because the total weight is 13
and the maximum we can carry is 7
. We can’t just take the first one because with value 1
because it is not the best profit.
How would you solve this problem?
First, we have to define what parameters are we going to use to make our greedy choice. This some ideas:
-
We can take items with the largest value in hopes to maximize profit. Based on that we can make take the last item and first having a total weight of 7 and a total cost of 8.
-
Also, we could take items smallest weight so we can fit as much as possible. Let’s analyze both options. So we can choose the first two items for a total value of 5 and a total weight of 4. This is worse than picking the largest value! 👎
-
One last idea, we can take items based on the best value/weight ratio and take fractions of an article to fill up the knapsack to maximum weight. In that case, we can buy the last item in full and 2/3 of the 2nd item. We get a total value of
9.67
and a total weight of7
. This heuristics seems to be the most profitable. 👍
{ value: 1, weight: 1 }, // 1/1 = 1 { value: 4, weight: 3 }, // 4/3 = 1.33 ✅ { value: 5, weight: 4 }, // 5/4 = 1.25 { value: 7, weight: 5 }, // 7/5 = 1.4 ✅
Let’s implement this algorithm!
link:../../../src/algorithms/knapsack-fractional.js[role=include]
What’s the runtime of this algorithm?
We have to sort the array based on value/weight ratio. Sorting runtime is O(n log n). The rest is linear operations, so we the answer is O(n log n) for our greedy algorithm.