Vibe Engines
YouTube

Vibe Engines is free, interactive AI engineering & system design — runs in your browser. Pick a learning path

CODING CHALLENGE · N°03

Fizz Buzz

Easy MathStringsWarm-up

The famous screening question. Print 1…n, but multiples of 3 become "Fizz", of 5 become "Buzz", and of both become "FizzBuzz". Easy — the catch is testing divisibility in the right order.

The problem

Return a list of length n where, for each i from 1 to n: it is "FizzBuzz" if i is divisible by both 3 and 5, "Fizz" if only by 3, "Buzz" if only by 5, otherwise the number as a string.

SOLVE IT YOURSELF

Your turn — write it

Edit the stub, hit Run (or ⌘/Ctrl + Enter), and watch the hidden tests. Stuck? open the hints under the problem, or Reveal solution is one click away.

YOUR TASK

Implement fizzbuzz(n): return the list of length n following the Fizz / Buzz / FizzBuzz rules. Watch the order — test "both" before either single case.

CPython · WebAssembly
Examples and constraints 4 examples
EXAMPLE 1
Input n = 5
Output ["1", "2", "Fizz", "4", "Buzz"]
EXAMPLE 2
Input n = 15
Output ["1", "2", "Fizz", "4", "Buzz", "Fizz", "7", "8", "Fizz", "Buzz", "11", "Fizz", "13", "14", "FizzBuzz"]
15 is divisible by 3 and 5
EXAMPLE 3
Input n = 3
Output ["1", "2", "Fizz"]
EXAMPLE 4
Input n = 1
Output ["1"]
edge case: the smallest n — the loop starts at 1, so there is no entry for 0
CONSTRAINTS
  • 1 ≤ n ≤ 10⁴
  • Every element is a string — including the plain numbers.
  • Check divisibility by 15 first (or combine the two booleans), or the FizzBuzz case never fires.
Hints 4 ideas
HINTS — 4 IDEAS
  1. Loop i from 1 to n inclusive.
  2. Divisible by both 3 and 5 is the same as divisible by 15 — check that first.
  3. Otherwise check 3, then 5, then fall back to the number.
  4. Everything in the list is a string, so convert the plain numbers too.
Approach, complexity & discussion — open after you solve

Start from brute force

The obvious first draft tests the rules in the order the problem states them: if i % 3 == 0 appends “Fizz”, elif i % 5 == 0 appends “Buzz”, elif i % 15 == 0 appends “FizzBuzz”, and otherwise the number. It runs in O(n) time with O(n) space for the list, and it is right for 14 of the first 15 numbers. It is wrong at 15 and at every multiple of 15 after it, because every multiple of 15 is also a multiple of 3: the first branch catches it, and an elif chain never looks further. Up to 10,000 this draft never prints “FizzBuzz” once. Speed was never the problem; the order of overlapping conditions is.

The approach

The insight is that the rules overlap, and an if/elif chain is decided by its first true branch, so the most specific rule has to come first. Divisible by both 3 and 5 is the same as divisible by 15, because 3 and 5 share no factor and 15 is their least common multiple, so one test, i % 15 == 0, covers the combined case. The invariant: after the iteration for i, out holds exactly i strings and out[i - 1] is the answer for i.

The reference loops for i in range(1, n + 1), so 1 and n are both included and there is no entry for 0. It checks i % 15 == 0 first and appends “FizzBuzz”, then i % 3 == 0 for “Fizz”, then i % 5 == 0 for “Buzz”, and otherwise appends str(i), because every element must be a string. An equally correct shape builds the word from two independent checks and falls back to the number when the word is empty; that version has no ordering trap at all.

fizzbuzz(15): the cycle repeats every 15test order1–56–1011–1512Fizz4BuzzFizz78FizzBuzz11Fizz1314FizzBuzz1. i % 15 == 02. i % 3 == 03. i % 5 == 015 is also a multiple of 3: test 3 first and it prints Fizzup to 10,000: 666 FizzBuzz, 2,667 Fizz, 1,334 Buzz, 5,333 numbers
Fizz cells (solid) fall every third cell, Buzz always lands in the last column because the rows are five wide, and plain numbers are dashed. Only the highlighted cell needs both words, and it can be reached only if the i % 15 test runs before the i % 3 test. Because the grid repeats every 15 numbers, the same picture describes every block of 15 up to n, which is also why the counts at the bottom are close to n/15, 4n/15 and 2n/15.

Walk through an example

The second example, n = 15, traced with the three remainders the chain can test; its first five rows are the first example.

stepi % 15i % 3i % 5branch takenappended
i = 1111else"1"
i = 2222else"2"
i = 3303i % 3 == 0"Fizz"
i = 4414else"4"
i = 5520i % 5 == 0"Buzz"
i = 6 to 14non-zero0 at 6, 9, 120 at 10Fizz at 6, 9, 12; Buzz at 10; else at 7, 8, 11, 13, 14"Fizz", "7", "8", "Fizz", "Buzz", "11", "Fizz", "13", "14"
i = 15000i % 15 == 0, tested first"FizzBuzz"
return————15 strings, the last one "FizzBuzz"

Complexity

Reference: O(n) time, a fixed number of modulo checks and one append per number, and O(n) space for the returned list; strictly, the digits written grow as O(n log n) characters. Nothing can beat O(n), because the output has n entries. Naive chain: the same O(n) time and space. Its problem is order, not cost.

Edge cases to test

  • n = 1: ["1"]. The loop starts at 1; starting at 0 adds a “FizzBuzz” for 0, because 0 % 15 == 0, and drops the last number.
  • n = 15: the last entry is “FizzBuzz”. A chain that tests 3 first returns “Fizz” there.
  • n = 30: indices 14 and 29 are both “FizzBuzz”. Every multiple of 15 must reach the first branch, not just 15 itself.
  • n = 2: ["1", "2"], strings rather than the integers [1, 2]. A list of ints fails the equality check.
  • n = 10⁴, the upper bound: 10,000 strings, of which 666 are “FizzBuzz”, 2,667 “Fizz”, 1,334 “Buzz” and 5,333 plain numbers.
  • n = 0, outside the stated range: range(1, 1) is empty, so the reference returns [] rather than raising.

Common mistakes

  • Checking 3 and 5 before 15, so multiples of 15 print “Fizz” instead of “FizzBuzz”.
  • Off-by-one on the range (starting at 0, or missing the last number).
  • Appending the plain numbers as integers: fizzbuzz(2) must be ["1", "2"], and [1, 2] is not equal to it.
  • Over-engineering a five-line problem: a rule table earns its place once there are several rules, but for two rules a plain loop that is obviously correct is the better answer.

Follow-ups an interviewer asks next

Add a rule: multiples of 7 print “Bazz”.
The if/elif chain stops scaling, because three rules have seven non-empty combinations, each needing its own branch in the right order. Build the word instead: keep an ordered list of (divisor, word) pairs, join the words whose divisor divides i, and fall back to str(i) when nothing matched. Then 21 gives “FizzBazz”, 35 “BuzzBazz” and 105 “FizzBuzzBazz” with no extra code.
Solve it without the modulo operator.
Keep two countdown counters, one starting at 3 and one at 5. Each iteration decrements both; when one reaches zero, add its word and reset it to 3 or 5. That replaces division with subtraction and produces exactly the same list, which you can confirm by comparing it with the modulo version for every n up to 10,000.
n is a billion. Do not build the list.
Yield each string from a generator, so the extra memory stays O(1) instead of O(n). You can also use the fact that the pattern repeats every 15 numbers: precompute the 15-entry cycle once and look up (i - 1) % 15, writing str(i) wherever the cycle has no word.
Count the Fizz, Buzz and FizzBuzz entries for any n without building the list.
Use inclusion and exclusion. FizzBuzz is n // 15. Fizz is n // 3 - n // 15, because the multiples of 15 were counted among the multiples of 3 and belong to FizzBuzz. Buzz is n // 5 - n // 15. The plain numbers are what is left: n - n // 3 - n // 5 + n // 15, where the last term adds back the multiples of 15 that were subtracted twice. For n = 10⁴ that gives 666, 2,667, 1,334 and 5,333, the same counts the full list produces, in O(1) time.

Where this shows up

FizzBuzz is the classic screening warm-up — it filters for basic control flow and, subtly, for handling the combined case in the right order. Trivial as it is, the divisibility-ordering trap is a real one that catches rushed candidates.

The trap itself, overlapping rules evaluated in order where the first match wins, is everywhere in production code, and the fix is always the same: put the most specific rule first, or make the rules independent so their order stops mattering.

  • Exception handlers. In Python, an except Exception placed above an except ValueError catches every ValueError first, and the specific handler never runs, with no warning. Java refuses to compile the same mistake, reporting that the subclass exception has already been caught.
  • Firewall and access rules. First-match rule lists behave like an elif chain: a broad “allow” placed above a narrow “deny” silently disables the deny.
  • Request routing. Routers that try routes in registration order send /users/me to a /users/:id handler registered above it, with id = "me".
  • IP routing. Routing tables avoid the problem by rule rather than by order: the longest matching prefix wins, so a /24 route always beats the /16 that contains it, wherever it sits in the table.

That last design is the build-the-word FizzBuzz in another form: specificity is decided by the data, not by where a line happens to be written, which is why it keeps working as rules are added. When you review a chain of overlapping conditions, ask of each branch whether any input that reaches it could also satisfy a branch above it.

Check yourself

How many entries of fizzbuzz(30) are exactly "Fizz"?

8. There are 10 multiples of 3 up to 30, but 15 and 30 are multiples of 15 and become “FizzBuzz” instead. The reference’s list for n = 30 has 8 Fizz, 4 Buzz and 2 FizzBuzz.

The naive chain tests 3, then 5, then 15. For n = 10,000, how many of its 10,000 entries are wrong?

Exactly 666, one per multiple of 15. Every other number takes the same branch in both orders, because the order only matters where two conditions are true at once, and here that is only the multiples of 15.

Change the rules to 4 (“Fizz”) and 6 (“Buzz”). A colleague tests i % 24 == 0 first, since 4 × 6 = 24. What does their version print for 12?

“Fizz”, which is wrong. The combined test must be the least common multiple, and lcm(4, 6) = 12 because 4 and 6 share the factor 2. For 3 and 5 the product happens to equal the LCM only because they share no factor.

Using the build-the-word version with rules (3, Fizz), (5, Buzz), (7, Bazz), what do 42, 70 and 105 give?

"FizzBazz", "BuzzBazz" and "FizzBuzzBazz". 42 = 2·3·7, 70 = 2·5·7, 105 = 3·5·7; each word is appended independently in rule order, so no combined case has to be written or ordered.

How many entries of fizzbuzz(100) are plain numbers?

53. There are 33 multiples of 3 and 20 multiples of 5, and the 6 multiples of 15 sit in both groups, so 33 + 20 − 6 = 47 entries get a word and 100 − 47 = 53 stay numbers. The same list has 27 Fizz, 14 Buzz and 6 FizzBuzz.

Reference solution

Reference solution — Python
def fizzbuzz(n):
    out = []
    for i in range(1, n + 1):
        if i % 15 == 0:          # both 3 and 5 — check this FIRST
            out.append("FizzBuzz")
        elif i % 3 == 0:
            out.append("Fizz")
        elif i % 5 == 0:
            out.append("Buzz")
        else:
            out.append(str(i))   # plain number, as a string
    return out


print(fizzbuzz(5))    # -> ['1', '2', 'Fizz', '4', 'Buzz']
Reference solution — TypeScript
function fizzbuzz(n: number): string[] {
  const out: string[] = [];
  for (let i = 1; i <= n; i++) {
    if (i % 15 === 0) {           // both 3 and 5 — check this FIRST
      out.push("FizzBuzz");
    } else if (i % 3 === 0) {
      out.push("Fizz");
    } else if (i % 5 === 0) {
      out.push("Buzz");
    } else {
      out.push(String(i));        // plain number, as a string
    }
  }
  return out;
}

console.log(fizzbuzz(5));         // -> ["1","2","Fizz","4","Buzz"]

Questions about Fizz Buzz

What is the FizzBuzz problem?

FizzBuzz asks you to go through the numbers from 1 to n and replace multiples of 3 with Fizz, multiples of 5 with Buzz, and multiples of both with FizzBuzz, leaving every other number as it is. It began as a children’s counting game and became a well-known programming screening question, because in a few lines it tests a loop, the modulo operator and the order of overlapping conditions. In this version you return the results as a list of strings, so the plain numbers are converted too.

Why do you check divisibility by 15 first in FizzBuzz?

Because the conditions overlap and an if-elif chain stops at the first true branch. Every multiple of 15 is also a multiple of 3, so if the test for 3 comes first, 15 gets Fizz and the FizzBuzz branch is never reached. Checking 15 first handles the most specific case before the general ones. Divisible by both 3 and 5 is the same as divisible by 15 because 3 and 5 share no common factor, which makes 15 their least common multiple.

How do you solve FizzBuzz without checking for 15?

Build the output from independent checks. Start with an empty string, add Fizz if the number is divisible by 3, add Buzz if it is divisible by 5, and if the string is still empty use the number itself. For 15 both checks fire and the two words join into FizzBuzz on their own. This version has no ordering trap, and adding another rule, such as Bazz for multiples of 7, is one more check instead of a new set of combined cases.

What is the time complexity of FizzBuzz?

Linear in n. Each number from 1 to n gets a fixed number of modulo checks and one append, so the loop does O(n) work, and the returned list holds n strings, so it also uses O(n) space. No algorithm can do better, because the output itself has n entries. If the strings are streamed with a generator instead of collected in a list, the extra memory drops to a constant. At the upper limit of n = 10,000, the list contains 666 FizzBuzz entries.

▶  Watch it explained
Finished this one? 0 / 76 Challenges done
NextTwo Sum Next on the “Interview classics” ladder · rung 2 of 10

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.