Cash

US coins

Problem to Solve

Suppose you work at a store and a customer gives you $1.00 (100 cents) for candy that costs $0.50 (50 cents). You’ll need to pay them their “change,” the amount leftover after paying for the cost of the candy. When making change, odds are you want to minimize the number of coins you’re dispensing for each customer, lest you run out (or annoy the customer!). In a file called cash.py, implement a program in Python that prints the minimum coins needed to make the given amount of change, in cents, as in the below:

Change owed: 25
1

But prompt the user for an integer greater than 0, so that the program works for any amount of change:

Change owed: 70
4

Re-prompt the user, again and again as needed, if their input is not greater than or equal to 0 (or if their input isn’t an integer at all!).

Demo

Greedy Algorithms

Fortunately, computer science has given cashiers everywhere ways to minimize numbers of coins due: greedy algorithms.

According to the National Institute of Standards and Technology (NIST), a greedy algorithm is one “that always takes the best immediate, or local, solution while finding an answer. Greedy algorithms find the overall, or globally, optimal solution for some optimization problems, but may find less-than-optimal solutions for some instances of other problems.”

What’s all that mean? Well, suppose that a cashier owes a customer some change and in that cashier’s drawer are quarters (25¢), dimes (10¢), nickels (5¢), and pennies (1¢). The problem to be solved is to decide which coins and how many of each to hand to the customer. Think of a “greedy” cashier as one who wants to take the biggest bite out of this problem as possible with each coin they take out of the drawer. For instance, if some customer is owed 41¢, the biggest first (i.e., best immediate, or local) bite that can be taken is 25¢. (That bite is “best” inasmuch as it gets us closer to 0¢ faster than any other coin would.) Note that a bite of this size would whittle what was a 41¢ problem down to a 16¢ problem, since 41 - 25 = 16. That is, the remainder is a similar but smaller problem. Needless to say, another 25¢ bite would be too big (assuming the cashier prefers not to lose money), and so our greedy cashier would move on to a bite of size 10¢, leaving him or her with a 6¢ problem. At that point, greed calls for one 5¢ bite followed by one 1¢ bite, at which point the problem is solved. The customer receives one quarter, one dime, one nickel, and one penny: four coins in total.

It turns out that this greedy approach (i.e., algorithm) is not only locally optimal but also globally so for America’s currency (and also the European Union’s). That is, so long as a cashier has enough of each coin, this largest-to-smallest approach will yield the fewest coins possible. How few? Well, you tell us!

Advice

Write some pseudocode before writing more code

If unsure how to solve the problem itself, break it down into smaller problems that you can probably solve first. For instance, this problem is really only a handful of problems:

  1. Prompt the user for change owed, in cents.
  2. Calculate how many quarters you should give customer. Subtract the value of those quarters from cents.
  3. Calculate how many dimes you should give customer. Subtract the value of those dimes from remaining cents.
  4. Calculate how many nickels you should give customer. Subtract the value of those nickels from remaining cents.
  5. Calculate how many pennies you should give customer. Subtract the value of those pennies from remaining cents.
  6. Sum the number of quarters, dimes, nickels, and pennies used.
  7. Print that sum.

This is the greedy algorithm you can use to solve this problem, so let’s write some pseudcode as comments to remind you to do just that:

def main():
    # Prompt the user for change owed, in cents

    # Calculate how many quarters you should give customer
    # Subtract the value of those quarters from cents

    # Calculate how many dimes you should give customer
    # Subtract the value of those dimes from remaining cents

    # Calculate how many nickels you should give customer
    # Subtract the value of those nickels from remaining cents

    # Calculate how many pennies you should give customer
    # Subtract the value of those pennies from remaining cents

    # Sum the number of quarters, dimes, nickels, and pennies used
    # Print that sum


main()
Convert the pseudocode to code

First, consider how you might prompt the user for the cents they are owed. A while loop with break is helpful when you want to do something at least once, and possibly again and again, as in the below:

def main():

    # Prompt the user for change owed, in cents
    while True:

        # Try to convert input cents to an integer 
        try:
            cents = int(input("Change owed: "))
        except ValueError:

            # If we encounter a `ValueError` in the above line, try the loop again from the top
            continue
        
        # Exit the loop only if cents is greater than or equal to 0
        if cents >= 0:
            break


main()

It’s wise to stop here and test your program. Test to be sure it reprompts you if you enter less than 0 cents (or if you enter an input like “cat”).

Since this is a hefty bit of code, it seems like we have a good opportunity for abstraction. Let’s more simply write a get_cents function that accomplishes the same task, but hides the complexity of it away from our main function:

def get_cents(prompt):
    ...

Notice that this function is indeed named get_cents per def get_cents. And per prompt in parentheses, it takes an input called prompt.

Consider simply moving the complexity of the code we’ve written to get_cents. Be sure to return cents at the end, so that this function can pass the cents we’ve gotten from the user back up to main:

def get_cents(prompt):

    # Prompt the user for change owed, in cents
    while True:

        # Try to convert input cents to an integer 
        try:
            cents = int(input(prompt))
        except ValueError:

            # If we encounter a `ValueError` in the above line, try the loop again from the top
            continue
        
        # Exit the loop only if cents is greater than or equal to 0
        if cents >= 0:
            break
    
    return cents

Now let’s integrate get_cents into main, such that our program looks like this:

def main():

    # Prompt the user for change owed, in cents
    cents = get_cents("Change owed: ")

    # Calculate how many quarters you should give customer
    # Subtract the value of those quarters from cents

    # Calculate how many dimes you should give customer
    # Subtract the value of those dimes from remaining cents

    # Calculate how many nickels you should give customer
    # Subtract the value of those nickels from remaining cents

    # Calculate how many pennies you should give customer
    # Subtract the value of those pennies from remaining cents

    # Sum the number of quarters, dimes, nickels, and pennies used
    # Print that sum


def get_cents(prompt):

    # Prompt the user for change owed, in cents
    while True:

        # Try to convert input cents to an integer
        try:
            cents = int(input(prompt))
        except ValueError:

            # If we encounter a `ValueError` in the above line, try the loop again from the top
            continue

        # Exit the loop only if cents is greater than or equal to 0
        if cents >= 0:
            break


main()

Next, consider how to calculate how many quarters you should give the customer. Since we’re using a greedy algorithm, this question becomes “what’s the greatest number of quarters could you give them?”. Thankfully, Python has a handy operator, //. // is similar to / in that it divides two numbers and returns the result (e.g., 51 // 25). However, unlike /, // tosses away the remainder of the division, only returning the whole number. For instance, 51 // 25 is not 2.04 but 2. And similarly, 74 // 25 is not 2.96, but also 2. In other words x // y tells us the greatest whole number of times y can “fit into” x.

def main():

    # Prompt the user for change owed, in cents
    cents = get_cents("Change owed: ")

    # Calculate how many quarters you should give customer
    quarters = cents // 25

    # Subtract the value of those quarters from cents
    cents = cents - (quarters * 25)

    # Calculate how many dimes you should give customer
    # Subtract the value of those dimes from remaining cents

    # Calculate how many nickels you should give customer
    # Subtract the value of those nickels from remaining cents

    # Calculate how many pennies you should give customer
    # Subtract the value of those pennies from remaining cents

    # Sum the number of quarters, dimes, nickels, and pennies used
    # Print that sum


def get_cents(prompt):

    # Prompt the user for change owed, in cents
    while True:

        # Try to convert input cents to an integer
        try:
            cents = int(input(prompt))
        except ValueError:

            # If we encounter a `ValueError` in the above line, try the loop again from the top
            continue

        # Exit the loop only if cents is greater than or equal to 0
        if cents >= 0:
            break


main()

A few problems down, and a few more to go! Notice a pattern you could re-use here?

How to Test

For this program, try testing your code manually. It’s good practice:

  • If you input -1, does your program prompt you again?
  • If you input 0, does your program output 0?
  • If you input 1, does your program output 1 (i.e., one penny)?
  • If you input 4, does your program output 4 (i.e., four pennies)?
  • If you input 5, does your program output 1 (i.e., one nickel)?
  • If you input 24, does your program output 6 (i.e., two dimes and four pennies)?
  • If you input 25, does your program output 1 (i.e., one quarter)?
  • If you input 26, does your program output 2 (i.e., one quarter and one penny)?
  • If you input 99, does your program output 9 (i.e., three quarters, two dimes, and four pennies)?