FizzBuzz, Redux

I’ve been applying for a lot of jobs lately, so naturally I have thoughts about interview questions. I’ve already written about one of those questions, and I eventually want to write about what makes an interview question good or bad. That said, it’s impossible to do that without talking about FizzBuzz, the greatest and most famous coding interview question ever.

For those who aren’t familiar with FizzBuzz, here it is:

Write a program that prints the numbers from 1 to 100. But for multiples of three print “Fizz” instead of the number and for the multiples of five print “Buzz”. For numbers which are multiples of both three and five print “FizzBuzz”.

https://imranontech.com/2007/01/24/using-fizzbuzz-to-find-developers-who-grok-coding/

The theory behind FizzBuzz is that many coders are bad because they fundamentally do not think like coders, and no amount of on-the-job training will get them to think like coders. (There’s nothing wrong with that–not having a coder mindset doesn’t make you an idiot–but it does probably mean you shouldn’t be coding.)

Furthermore, the theory goes, is that you can filter such people out of your interview process by giving them a slightly atypical problem that is very easy and incorporates basic elements of coding. The problem is atypical in the sense that it’s not something you’d normally do and the solution doesn’t look like typical code in a typical codebase. This means they can’t rehash things they’ve seen on StackOverflow a zillion times; they need to actually use their brain, albeit minimally. FizzBuzz’s solution is inelegant, but straightforward. When faced with a slightly unfamiliar challenge, the bad coders will crumble. Meanwhile, since the problem is so simple and fair, the halfway decent coders get FizzBuzz right 100% of the time in a few minutes, even on a bad day with zero coffee.

FizzBuzz is a victim of its own success: it’s so effective at filtering out people who can’t code their way out of a paper bag that it became a bit famous, and now people who can’t code their way out of a paper bag are familiar with this problem due to that fame. These days, the good interview problems designed to filter out the bad candidates are more “FizzBuzz-esque” or “FizzBuzz-inspired” rather than actual FizzBuzz.

In Search of a Pythonic FizzBuzz

You’d think that the problem’s popularity would mean that finding good answers would be easy. Ironically, googling for “fizzbuzz python” gives you some terrible examples of FizzBuzz, written by people who aren’t writing Pythonic, professional production-ready code that makes full use of all of Python’s capabilities. Here’s what a typical example looks like:

Bad FizzBuzz

for i in range(1, 101):
    if i % 3 == 0 and i % 5 == 0:
        print("FizzBuzz", end=" ")
    elif i % 3 == 0:
        print("Fizz", end=" ")
    elif i % 5 == 0:
        print("Buzz", , end=" ")
    else:
        print(i, end=" ")

This is the most common Python solution to FizzBuzz, and while it works, it’s bad. I’m not saying you should filter candidates out of your process for giving this answer, but we can do so, so, so much better!

The first thing that must be noted immediately off the bat is that i % 3 == 0 and i % 5 == 0 can be replaced with i % 15 == 0. However, this isn’t the main issue with the Bad FizzBuzz.

The main reason it sucks is because we have a zillion print() statements in the code; we should be defining a string s instead and printing that at the end. While this change will not immediately improve the code, this will make improvements down the line easier to see, easier to make, and easier to maintain.

Why? It’s usually better to create an output and then, as a last step, output it. The Bad FizzBuzz doesn’t do this; it has four different lines devoted to “outputs,” gated by if statements. Or, put another way: the structure of the code feels less like an assembly line where every iteration’s output is presented at the end, and more like the Plinko game from The Price is Right where your output magically falls into some bucket. In most cases, it’s better to design your code more like an assembly line and less like Plinko. This gets us to:

Meh FizzBuzz

for i in range(1, 101):
    if i % 15 == 0:
        s = "FizzBuzz"
    elif i % 3 == 0:
        s = "Fizz"
    elif i % 5 == 0:
        s = "Buzz"
    else:
        s = i
    print(s, end=" ")

It may not look like it, but we’ve overcome a huge conceptual roadblock present in the Bad FizzBuzz: when every iteration ends with a single string s, we can start thinking of better ways to create s. Before, we couldn’t do that because we weren’t “creating” an s so much as we were assigning each iteration to a bucket. I know I sound like a broken record, but this framing is extremely important, and a big difference between amateurs and pros is instinctively knowing why having fewer “exit points” is so important.

Let’s work with the fact that "FizzBuzz" is just "Fizz" + "Buzz". What if we start each iteration with an empty string and add to it, and situations where you’d get "FizzBuzz" occur by concatenation of two strings instead of by fiat? Of course, you’d still have to check at the end if the string is empty, then turn the string into the number. Now we have:

Alright FizzBuzz

for i in range(1, 101):
    s = ""
    if i % 3 == 0:
        s += "Fizz"
    if i % 5 == 0:
        s += "Buzz"
    if s == "":
        s = i
    print(s, end=" ")

It’s starting to look good, but it’s not quite “Pythonic.” You might be thinking the issue is that not s is the same as s == "" when dealing with variables we know are strings, but the former is more Pythonic, so we should default toward that.

But wait, there’s an even better trick that we can use: Python’s or logic operator. Bear with me for a second.

A common misconception is that the way or works is that it just returns True if one of the things on either side of the or is True. And yes, it in fact does that when you’re working with booleans, but that’s not what’s really happening under the hood. Allow me to blow (some of) your minds:

>>> 0 or 1
1
>>> None or 1
1
>>> 0 or 4
4
>>> 2 or 4
2
>>> None or 4
4
>>> "" or 4
4
>>> False or 4
4
>>> 0 or ""
''
>>> "" or 0
0

See what’s going on? What the or operator actually does in Python is skip things that are False until it hits something that is True (or the last thing if nothing comes up true), then it returns that. An empty string’s boolean value is False.

So the FizzBuzz problem says you’re supposed to return the number if none of the conditions are met. How do we know if none of the conditions are met? It’s when the string s is empty. But wait, s or i should return i when s == ""… right? … Yes! This leads us to:

Almost There FizzBuzz

for i in range(1, 101):
    s = ""
    if i % 3 == 0:
        s += "Fizz"
    if i % 5 == 0:
        s += "Buzz"
    print(s or i, end=" ")

Now this is what I am talking about. We’ve turned an inelegant solution into something that almost looks like real Python by exploiting some of Python’s built-in capabilities.

… Can we do even better though?

Yes.

First of all, we don’t need to print() 100 times; we can just create a giant string that gets printed all in one go. If you’re thinking this means building segments and delimiting them with s += " ", then you’re not thinking Peak Python.

Secondly, even though the code is starting to look more Pythonic, it’s also much slower than the original code we had. The way we’re concatenating strings with += is a big source of this slowdown: because strings are immutable, concatenation requires creating additional (and unnecessary) temporary objects. In general, you should avoid concatenating strings with +=.

The solution to both of these problems is to use " ".join().

The join() method takes a list as an input, which means (thank the lord!) we can use a list comprehension! For those not in the know, that’s when you do something like [i for i in range(5)] which gives you [0, 1, 2, 3, 4].

Now if you spend a little time thinking about how you’d do this with a list comprehension, you might find it easier to think of a solution that utilizes i % 15 == 0, such as this:

Pythonic FizzBuzz

print(" ".join([
    "FizzBuzz" if i % 15 == 0 else
    "Fizz" if i % 3 == 0 else
    "Buzz" if i % 5 == 0 else
    str(i)
    for i in range(1, 101)
]))

This works, and it’s beautiful. That said, we spent so much time and energy trying to avoid specifically defining "FizzBuzz" as a number divisible by 15 that it feels like we gave something up for this answer. Fortunately, it doesn’t have to be this way: Using either "{0}{1}".format() or "%s%s" % () lets us replicate the functionality of the incremental operator. Hoorah! And there we have it:

Expert-Tier Pythonic FizzBuzz

print(" ".join([
    "{0}{1}".format(
        "Fizz" if i % 3 == 0 else "",
        "Buzz" if i % 5 == 0 else ""
    )
    or str(i) for i in range(1, 101)
]))

In awe at the Pythonicness of this lad, an absolute unit. This is, as far as I can tell, the best “serious” Python solution to FizzBuzz.

For the record, both this and the previous answer are 1-liners, with some cosmetic whitespace to make it more legible. Here it is without that whitespace:

print(" ".join(["{0}{1}".format("Fizz" if i % 3 == 0 else "", "Buzz" if i % 5 == 0 else "") or str(i) for i in range(1, 101)]))

For most job interviews, this solution (with the whitespacing) will blow the interviewer away. FizzBuzz solutions are typically ugly, and this somehow manages to not be ugly. That’s Python for ya.

Deep into FizzBuzz

Now that we’ve gotten FizzBuzz to be as small as possible, can we think bigger and better? Can we specialize and tailor our FizzBuzz solution to meet the business needs of whatever job we’re applying to?

What if we’re applying for a software engineer position? A static solution to FizzBuzz isn’t thinking big enough; we’ll need a FizzBuzz implementation that’s modular and flexible. Something that can turn all of our functions into FizzBuzz. How about a decorator?

We can turn a class into a decorator with Python’s __call__() magic method. Every time the decorator is used, we want to monkeypatch print such that print(3) will become print("Fizz"), print(5) will become print("Buzz"), and so on. To safely monkeypatch print, we’d want to use a context manager.

(If you’re still confused at what’s going on, I wrote another blog post that does something similar to this and goes into more detail, except in that post I made the decorator with a function instead of a class.)

Anyway, here’s the code for that:

Generalized + Portable FizzBuzz

import builtins

class fizzbuzz(object):
    
    class _ContextManager(object):
        
        def __init__(self):
            self._print = print
        
        def _custom_print(self, s, *args, **kwargs):
            new_s = "{0}{1}".format(
                "Fizz" if int(s) % 3 == 0 else "",
                "Buzz" if int(s) % 5 == 0 else ""
            ) or str(s)
            self._print(new_s, *args, **kwargs)
        
        def __enter__(self):
            builtins.print = self._custom_print
        
        def __exit__(self, *args):
            builtins.print = self._print
    
    def __init__(self, f):
        self.func = f
    
    def __call__(self, *args, **kwargs):
        with self._ContextManager():
            return self.func(*args, **kwargs)

@fizzbuzz
def print_some_numbers():
    for i in range(1, 101):
        print(i, end=" ")

print_some_numbers()

Wow! Now we can make all our functions into FizzBuzz solutions with one extra line of code before all of our functions, i.e. @fizzbuzz. This will surely knock the socks off any interviewer for whatever software engineering job you’re interviewing for. Just be sure to add unit-tests and documentation.

Is your SWE more on the web development side? Be sure to show them you know your way around Flask by building an API for your FizzBuzz solution. We’re going to run the website on the default 127.0.0.1:5000, and we don’t need anything other than a get() in our API class. A bare bones solution sufficient for a job interview might look something like this:

FizzBuzz RESTful API

The API:
from flask import Flask, request
from flask_restful import Api, Resource

def fizzbuzz(i):
    return \
        "{0}{1}".format(
            "Fizz" if i % 3 == 0 else "",
            "Buzz" if i % 5 == 0 else ""
        ) or str(i)

class FizzBuzzAPI(Resource):
    
    def get(self):
        start = int(request.args.get("start"))
        end = int(request.args.get("end"))
        return {"fizzbuzz" : [
            {
                "id" : i,
                "value" : fizzbuzz(i)
            }
            for i in range(start, end + 1)
        ]}

app = Flask(__name__)
api = Api(app)
api.add_resource(FizzBuzzAPI, "/fizzbuzz")

app.run()
The getter:
import requests

fb = requests.get("http://127.0.0.1:5000/fizzbuzz?start=1&end=100")
print(" ".join([i["value"] for i in fb.json()["fizzbuzz"]]))

Awesome! Now we’re qualified for web dev! But what if you’re not applying for either a SWE job or a web dev job, but a data analysis job? Don’t let this opportunity to show off your pandas skills go to waste!

Our goal is to print out 100 rows from a pd.Series object. We could simply do something like this:

Boring Pandas FizzBuzz

import pandas as pd

fizzbuzz_series = \
    pd.Series(range(1,101)) \
    .apply(
        lambda x :
            "{0}{1}".format(
                "Fizz" if x % 3 == 0 else "",
                "Buzz" if x % 5 == 0 else ""
            ) or str(x)
    )
print(" ".join(list(fizzbuzz_series)))

The problem with this solution is that it is extremely boring and won’t score brownie points with the interviewer. You can do this without lambda and .apply() trickery, and stick entirely to built-in pandas stuff.

The way I think we should do this is to create 4 boolean columns, and transform it with pd.Series(pd.Categorical()). Then replace all the values corresponding to the numeric category with the index. Once you deal with some fussy stuff and cosmetic considerations, you get to this:

The Real Pandas FizzBuzz

import pandas as pd

df = pd.DataFrame({'i' : [i for i in range(101)]})
df['FizzBuzz'] = df['i'] % 15 == 0
df['Fizz'] = (df['i'] % 3 == 0) & ~(df['FizzBuzz'])
df['Buzz'] = (df['i'] % 5 == 0) & ~(df['FizzBuzz'])
df['num'] = ~(df['FizzBuzz'] | df['Fizz'] | df['Buzz'])
df = df.set_index('i')
df['output'] = \
    pd.Series(pd.Categorical(
        df.stack()[df.stack().astype("bool")]
        .index
        .get_level_values(1)
    )).astype(str)
df.loc[df['output'] == "num", 'output'] = \
    df.index.to_series().apply(str)
df = df.drop(0)
print(" ".join(list(df['output'])))

This will get you an entry-level data analysis job, but it’s not good enough for a data science. What you really want is a sklearn model that can predict (out of sample!) the correct FizzBuzz solution.

Of course, this is a problem easily solved with multinomial logistic regression, which is basically how every machine learning problem is solved. The most important part is defining our domain. *Pushes up glasses* There seems to be a cyclicality to the Fizzes and Buzzes, which in turn means a relationship between the remainder after dividing the number in the sequence, and whether it returns Fizz or Buzz or FizzBuzz. Since we’re not entirely sure what that relationship is and we’re trying to predict it, we’ll define each x_i to be [i, i%2, i%3, i%4, i%5, i%6] to include 5 different types of cyclicalities.

We’re going to cheat a little bit, and instead of having y_hat return either a number or a string, it’ll return one of four strings, which we’ll transcribe as being a number in the last step. We’re also going to use solver="newton-cg", which performs better than BFGS optimization.

Predictive FizzBuzz Machine Learning

import numpy as np
from sklearn.linear_model import LogisticRegression

SAMPLE_SIZE = 10000

def fizzbuzz(i):
    return \
        "{0}{1}".format(
            "Fizz" if i % 3 == 0 else "",
            "Buzz" if i % 5 == 0 else ""
        ) or "num"

def domain(i):
    return [i, i%2, i%3, i%4, i%5, i%6]

y = np.array([fizzbuzz(i) for i in range(101,101+SAMPLE_SIZE)])
X = np.array([domain(i) for i in range(101,101+SAMPLE_SIZE)])

clf = LogisticRegression(multi_class="multinomial", solver="newton-cg")
model = clf.fit(X, y)

X_test = np.array([domain(i) for i in range(1, 101)])
y_hat = model.predict(X_test)

print(" ".join([
    str(n+1) if val == "num" else val
    for n, val in enumerate(list(y_hat))
]))

Multinomial logistic regressions not fancy enough to beat the deluge of Ph.D.’s applying to the extremely competitive data scientist position? Then you’ll need to bring out the big guns: a neural network!

Someone else already did this, and instead of trying to reinvent this gigantic wheel I’ll just link to it here:

FizzBuzz Neural Network

https://github.com/joelgrus/fizz-buzz-tensorflow/

Of course, while it’s impressive, there’s a chance the neural network gets the answer wrong. If you don’t want to take any risks, why not just… hmmm, why didn’t we think of this sooner?

Hardcoded FizzBuzz

print("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")

Last but not least, while it’s not Python, it’s still, without a doubt the best FizzBuzz solution ever:

The Definitive Best FizzBuzz

https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition

And there you have it, just about every solution under the sun to FizzBuzz.

Special Thanks

@cubeinacube came up with the idea of using "{0}{1}".format() as a way to combine the " ".join() solution and the s or i solution. Without him, I’d most likely not have the very excellent Expert-Tier Pythonic FizzBuzz solution, which in turn was used in most of the subsequent flashy solutions.

<span>%d</span> bloggers like this: