Skip to content
ByteStepStart with recursion

Greedy algorithms, and when they are wrong

Free, no account

A greedy algorithm takes whatever looks best right now and never takes it back. No alternatives are kept, nothing is remembered, and there is only ever one pass over the problem. That is why greedy algorithms are the fastest things in the subject, and it is also the reason they can be wrong.

Below is the most familiar one there is: making change by always handing over the largest coin that fits. Start with everyday coins and it does exactly what you would do yourself, using the fewest coins possible every time.

Then press odd, which removes the 5 and changes nothing else. Not one character of the algorithm is different. It now makes 30 with 6 coins, where 3 would have done.

1function change(coins, amount) {
2 const purse = []
3 for (const coin of coins) { // largest first
4 while (coin <= amount) { // does it fit?
5 amount -= coin // take it
6 purse.push(coin)
7 }
8 }
9 return purse
10}

The purse

drawn by value; faded means too big for what is left

Making 63

  1. 25
  2. 10
  3. 5
  4. 1

63 still owed

Handed over

one slot per coin, so the total is countable

0 coins

Making 63 from 25, 10, 5, 1. The rule is simple: always take the largest coin that fits.

1/8
Detail
8 steps

The rule, the step that ruins everything after it, and why one failing case settles the question.

The rule, and why it is so fast

Look at the coins largest first. If one fits in what is still owed, take it and keep taking it while it fits. When it no longer does, move down a size. Stop when nothing is owed.

There is no list of possibilities anywhere, no going back, and nothing to store beyond the running total. That is the appeal: a greedy algorithm usually costs a single pass, where working out the genuinely best answer means considering combinations.

Where it breaks

Watch the odd purse make 30. The 25 fits, so it is taken, which leaves 5. There is no 5 any more, so 5 becomes five 1s, and the total is 6 coins. Three 10s were sitting there the whole time.

Nothing about that first step was careless. Taking the 25 was the locally best move: it is the largest coin that fits, it reduces what is owed more than any other single choice, and judged on its own it is unarguable. It is still the reason the answer is worse.

That is the shape of every greedy failure. Not a bad step, but a good step whose cost only appears later, when the algorithm is no longer looking.

One case is enough

Removing the 5 breaks the same code on 30 of the 99 amounts below 100, starting at 30. Drag the amount and you will find them; they are not rare.

But even one would have been enough. A rule that is right almost always is not right, and a single counterexample settles it. This is why greedy algorithms are the part of the subject where people bother with proofs: testing shows you the cases you thought of, and the coin sets that break greedy are exactly the ones nobody thinks of.

Know someone stuck on this? Send it to them.

A classmate, a study group, someone learning this on their own at midnight. The link opens the lesson set up exactly as you have it, the same function and the same number, and runs from the first step so they watch the whole thing build rather than landing in the middle of it.

Also in this course

Big-O: what “grows faster” actually means

A greedy rule is cheap because it makes one pass. That sentence only means something once you can compare one pass with the alternatives.

Start there →