Categories
Career Programming

Programmer interview challenge 2: The dreaded FizzBuzz, in Python

The “job interview” scene from Good Will Hunting.

Spotting the programmers who can’t program

Sooner or later, you will encounter — or worse still, end up working with — a programmer who only seems good at programming. This person will have an impressive-looking resume. They’ll know all the proper terminology, be able to speak intelligently about the key concepts in this programming language or that framework or library, and may even have given a pretty good talk at a meetup or conference. But when they’re put to the task of actually writing working software, they just can’t do it.

These aren’t programmers who have difficulty taking on big problems, such as the kind you run into when working on complex problems and writing all-new code from scratch. They’re not even programmers who run into trouble just working on the sort of everyday problems that you encounter maintaining established, working software. These are programmers who can’t solve simple problems, the sort that you should be able to do during a lunch or coffee break. They might be good in other roles in the development process, but not in one where they have to write production code.

As a result, there’s a category of little assignments whose sole purpose isn’t to identify great programmers, or even good ones, but to spot the ones you shouldn’t hire. The best known of these is the dreaded FizzBuzz.

FizzBuzz, explained

Fizzbuzz is an exercise that then-developer Imran Ghory wrote about back in 2007. The programmer is asked to implement a program — often in the language of their choice — that prints the numbers from 1 to 100, but…

  1. If the number is a multiple of 3, print Fizz instead of the number.
  2. If the number is a multiple of 5, print Buzz instead of the number.
  3. If the number is a multiple of both 3 and 5, print FizzBuzz instead of the number.

Simply put, its output should look something like this:

1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz, 16, 17, Fizz, 19, Buzz, Fizz, 22, 23, Fizz, Buzz, 26, Fizz, 28, 29, FizzBuzz, 31, 32, Fizz, 34, Buzz, Fizz, 37, 38, Fizz, Buzz, 41, Fizz, 43, 44, FizzBuzz, 46, 47, Fizz, 49, Buzz, Fizz, 52, 53, Fizz, Buzz, 56, Fizz, 58, 59, FizzBuzz, 61, 62, Fizz, 64, Buzz, Fizz, 67, 68, Fizz, Buzz, 71, Fizz, 73, 74, FizzBuzz, 76, 77, Fizz, 79, Buzz, Fizz, 82, 83, Fizz, Buzz, 86, Fizz, 88, 89, FizzBuzz, 91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz, Buzz.

In his blog entry on FizzBuzz, Imran wrote:

Most good programmers should be able to write out on paper a program which does this in a under a couple of minutes.

Want to know something scary ? – the majority of comp sci graduates can’t. I’ve also seen self-proclaimed senior programmers take more than 10-15 minutes to write a solution.

I’m not saying these people can’t write good code, but to do so they’ll take a lot longer to ship it. And in a business environment that’s exactly what you don’t want.

Let’s look at a couple of Python implementations.

The dumb-as-a-bag-of-hammers solution

If you think your interviewer has a sense of humor, you might try throwing this solution at them. I put this in a file named fizzbuzz.py:

def fizzBuzzDumb():
  return "1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz, 16, 17, Fizz, 19, Buzz, Fizz, 22, 23, Fizz, Buzz, 26, Fizz, 28, 29, FizzBuzz, 31, 32, Fizz, 34, Buzz, Fizz, 37, 38, Fizz, Buzz, 41, Fizz, 43, 44, FizzBuzz, 46, 47, Fizz, 49, Buzz, Fizz, 52, 53, Fizz, Buzz, 56, Fizz, 58, 59, FizzBuzz, 61, 62, Fizz, 64, Buzz, Fizz, 67, 68, Fizz, Buzz, 71, Fizz, 73, 74, FizzBuzz, 76, 77, Fizz, 79, Buzz, Fizz, 82, 83, Fizz, Buzz, 86, Fizz, 88, 89, FizzBuzz, 91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz, Buzz."

Note that I wrote this as a function that returns a string instead of as just a print statement. There’s a reason for this — as a function, it’s testable.

Let’s create a file for FizzBuzz tests. I called mine test_fizzbuzz.py. It’s also pretty dumb — all it does it confirm that fizzBuzzDumb() spits out the right result. It’s pretty much guaranteed to pass, since I copied and pasted the string from fizzBuzzDumb() into the constant that the test uses to confirm that the output is correct:

import pytest
from fizzbuzz import fizzBuzzDumb

fizzBuzz1To100Result = "1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz, 16, 17, Fizz, 19, Buzz, Fizz, 22, 23, Fizz, Buzz, 26, Fizz, 28, 29, FizzBuzz, 31, 32, Fizz, 34, Buzz, Fizz, 37, 38, Fizz, Buzz, 41, Fizz, 43, 44, FizzBuzz, 46, 47, Fizz, 49, Buzz, Fizz, 52, 53, Fizz, Buzz, 56, Fizz, 58, 59, FizzBuzz, 61, 62, Fizz, 64, Buzz, Fizz, 67, 68, Fizz, Buzz, 71, Fizz, 73, 74, FizzBuzz, 76, 77, Fizz, 79, Buzz, Fizz, 82, 83, Fizz, Buzz, 86, Fizz, 88, 89, FizzBuzz, 91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz, Buzz."

def test_dumb_fizzBuzz():
  result = fizzBuzzDumb()
  assert result == fizzBuzz1To100Result, f"The dumb solution returned the wrong result:\nExpected: {fizzBuzz1To100Result}\nActual: {result}."

With fizzbuzz.py and test_fizzbuzz.py in hand, I ran the test, and it unsurprisingly passed:

Working towards a real solution

The multiple problem

The first stumbling block I’ve seen people trying to write FizzBuzz is that they have no idea how to tell if a number x is a multiple of some other number y. This happens particularly often when the programmer doesn’t come from a math background.

(There’s a bit of math snobbery and bias in classical computer science. Some of it is just general academic snobbery, and some of it is from the fact that computer science wasn’t originally its own field of study in universities, but often a branch of the math or engineering department.)

To solve this problem, you want to use the modulo operator — the % symbol. It performs integer division, but instead of giving you the quotient (the result of a division), it gives you the remainder.

For example 3 % 2 gives you a result of 1. That’s because after dividing 3 by 2, you get a remainder of 1. 5 % 2 also gives you a result of 1, because 2 goes into 5 twice, leaving you a remainder of 1. 9 % 5 gives you a result of 4, as 5 goes into 9 once, leaving a remainder of 4.

If a division operation results in no remainder, % returns a result of 0. For instance 2 % 2, 4 % 2, 6 % 2, 8 % 2, and 10 % 2 all return a result of zero, since they’re all even numbers, which are all evenly divisible by 2. Another way of putting it is to say that they’re multiples of 2.

With this in mind, we can easily come up with a couple of statements that test if a number is a multiple of 3 and if a number is a multiple of 5:

isMultipleOf3 = (number % 3 == 0)
isMultipleOf5 = (number % 5 == 0)

Fizz, Buzz, FizzBuzz, or number?

This is the part that really trips up the weak programmers. It doesn’t follow this simple decision structure:

  • If condition A is met:
    • Perform task X.
  • Otherwise, if condition B is met:
    • Perform task Y.
  • Otherwise, if condition C is met:
    • Perform task Z.
  • Otherwise, if none of the above conditions are met:
    • Perform the default task.

FizzBuzz’s decision structure is more like this:

  • If condition A is met:
    • Perform task X.
  • Otherwise, if condition B is met:
    • Perform task Y.
  • But if both condition A and B are met:
    • Don’t perform task X or Y. Perform task Z instead.
  • Otherwise, if none of the above conditions are met:
    • Perform the default task.

This seems like a minor change, but it’s enough to make FizzBuzz a way of filtering out people might have serious trouble writing production software.

I’ve demonstrated live-coding Fizzbuzz in a number of presentations, and the approach I usually take is summarized in this Python code:

if isMultipleOf3 and isMultipleOf5:
  currentResult = "FizzBuzz"
elif isMultipleOf3:
  currentResult = "Fizz"
elif isMultipleOf5:
  currentResult = "Buzz"
else:
  currentResult = str(number)

At the end of this if statement, currentResult contains one of the following: FizzBuzz, Fizz, Buzz, or a number.

When live-coding in front of an audience — which is pretty much what a technical interview is — you want to keep a couple of things in mind:

  • You want to use the code to communicate your intent to the audience as clearly as possible.
  • Complexity is your enemy. You want to make the simplest thing that works.

My approach was to do handle the trickiest case first. My if statement handles the case where the number is both a multiple of 3 and a multiple of 5 first, followed by the individual multiple cases, followed by the default case. It’s simple, it’s easy to follow, and best of all, it works.

Putting it all together

Here’s the fizzBuzz() function that I wrote. I put it in fizzbuzz.py, just after the definition of fizzBuzzDumb():

def fizzBuzz(first = 1, last = 100):
  finalResult = ""

  for number in range(first, last + 1):
    currentResult = ""
    isMultipleOf3 = (number % 3 == 0)
    isMultipleOf5 = (number % 5 == 0)

    if isMultipleOf3 and isMultipleOf5:
      currentResult = "FizzBuzz"
    elif isMultipleOf3:
      currentResult = "Fizz"
    elif isMultipleOf5:
      currentResult = "Buzz"
    else:
      currentResult = str(number)

    finalResult += currentResult

    if number < last:
      finalResult += ", "
    else:
      finalResult += "."

  return finalResult

Here’s the full text of fizzbuzz.py:

def fizzBuzzDumb():
  return "1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz, 16, 17, Fizz, 19, Buzz, Fizz, 22, 23, Fizz, Buzz, 26, Fizz, 28, 29, FizzBuzz, 31, 32, Fizz, 34, Buzz, Fizz, 37, 38, Fizz, Buzz, 41, Fizz, 43, 44, FizzBuzz, 46, 47, Fizz, 49, Buzz, Fizz, 52, 53, Fizz, Buzz, 56, Fizz, 58, 59, FizzBuzz, 61, 62, Fizz, 64, Buzz, Fizz, 67, 68, Fizz, Buzz, 71, Fizz, 73, 74, FizzBuzz, 76, 77, Fizz, 79, Buzz, Fizz, 82, 83, Fizz, Buzz, 86, Fizz, 88, 89, FizzBuzz, 91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz, Buzz."

def fizzBuzz(first = 1, last = 100):
  finalResult = ""

  for number in range(first, last + 1):
    currentResult = ""
    isMultipleOf3 = (number % 3 == 0)
    isMultipleOf5 = (number % 5 == 0)

    if isMultipleOf3 and isMultipleOf5:
      currentResult = "FizzBuzz"
    elif isMultipleOf3:
      currentResult = "Fizz"
    elif isMultipleOf5:
      currentResult = "Buzz"
    else:
      currentResult = str(number)

    finalResult += currentResult

    if number < last:
      finalResult += ", "
    else:
      finalResult += "."

  return finalResult

This new function calls for a new test. Here’s the updated test_fizzbuzz.py:

import pytest
from fizzbuzz import fizzBuzzDumb, fizzBuzz

fizzBuzz1To100Result = "1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz, 16, 17, Fizz, 19, Buzz, Fizz, 22, 23, Fizz, Buzz, 26, Fizz, 28, 29, FizzBuzz, 31, 32, Fizz, 34, Buzz, Fizz, 37, 38, Fizz, Buzz, 41, Fizz, 43, 44, FizzBuzz, 46, 47, Fizz, 49, Buzz, Fizz, 52, 53, Fizz, Buzz, 56, Fizz, 58, 59, FizzBuzz, 61, 62, Fizz, 64, Buzz, Fizz, 67, 68, Fizz, Buzz, 71, Fizz, 73, 74, FizzBuzz, 76, 77, Fizz, 79, Buzz, Fizz, 82, 83, Fizz, Buzz, 86, Fizz, 88, 89, FizzBuzz, 91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz, Buzz."

def test_dumb_fizzBuzz():
  result = fizzBuzzDumb()
  assert result == fizzBuzz1To100Result, f"The dumb solution returned the wrong result:\nExpected: {fizzBuzz1To100Result}\nActual: {result}."

def test_fizzBuzz():
  result = fizzBuzz()
  assert result == fizzBuzz1To100Result, f"The dumb solution returned the wrong result:\nExpected: {fizzBuzz1To100Result}\nActual: {result}."

With fizzbuzz.py and test_fizzbuzz.py revised, I ran the test, and it passed again:

You can download fizzbuzz.py and test_fizzbuzz.py here (2KB, zipped folder with 2 Python files).

What’s next

In the next installment in this series, we’ll look at a different approach to coding FizzBuzz: functional programming!

Recommended reading

Previously, in the “Programmer interview challenge” series

3 replies on “Programmer interview challenge 2: The dreaded FizzBuzz, in Python”

Comments are closed.