What's wrong with this coin change python implementation?

Question:

I’m reading through Structure and Interpretation of Computer Programs, and on section 1.2.2 there’s a brute force implementation of the coin change problem given:

(define (count-change amount)
  (cc amount 5))

(define (cc amount kinds-of-coins)
  (cond ((= amount 0) 1)
        ((or (< amount 0) (= kinds-of-coins 0)) 0)
        (else (+ (cc amount
                     (- kinds-of-coins 1))
                 (cc (- amount
                        (first-denomination kinds-of-coins))
                     kinds-of-coins)))))

(define (first-denomination kinds-of-coins)
  (cond ((= kinds-of-coins 1) 1)
        ((= kinds-of-coins 2) 5)
        ((= kinds-of-coins 3) 10)
        ((= kinds-of-coins 4) 25)
        ((= kinds-of-coins 5) 50)))

On Leetcode, the same problem is asked as such:

Input: coins = [1,2,5], amount = 11

Output: 3

Explanation: 11 = 5 + 5 + 1

I attempted doing a literal translation of the scheme code into Python but I am not getting the result I expected. In particular, I don’t understand why this code is not giving me the same results as the SICP code even though both seem so similar! the only big difference I can see here is that, since the coins are given as a list instead of a hash map I can choose from, I iterate over each number by popping each number from the list.

def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0: return 1
        if amount < 0 or not coins: return 0

        # do not choose current coin
        return (self.coinChange(coins[1:], amount) + 
            self.coinChange(coins, amount - coins[0])) # choose current coin.
Asked By: Alejandro

||

Answers:

tl;dr: Order matters.

Input: coins = [1, 2, 5]

SICP
is greedily trying the 50-cent piece first, and eventually gets down to the penny.

Your code is
trying
the penny first, and hilarity ensues.

Debugging tip: log amount + returned value at each function entry + exit,
or formulate the problem as "return a
Counter
for each coin type" and then write a trivial helper
which counts up total coins.

Answered By: J_H

Your translation of the Scheme program into Python was perfect, including your extension of it to use a list of denominations instead of hardcoding it as SICP does (which it does because it hasn’t introduced lists yet at this point in the text).

The problem is, the program you translated is solving a different problem than the Leetcode exercise asks you to solve. Leetcode asks for the minimum number of coins you need in order to make change. SICP determines how many different ways you can legally make change, regardless of how many coins you spend in each way.

I necessarily disagree with the answer by J_H. The SICP code isn’t greedy, and order doesn’t matter. It does matter in a typical answer to the Leetcode exercise, but again, that isn’t what the code you translated is doing.

Answered By: amalloy
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.