Codewars #2: Code Katas Apr '25

Stella MarieStella Marie
13 min read

Fizz/Buzz

Kyu: 7
Go to description

In the traditonal fizz-buzz, a function given a number outputs either a string of fizz, buzz, fizz buzz, or something else for each number in the sequence, or a string of one of these words. To determine which for what numbers, fizz is for numbers divisible by 3, buzz for numbers divisible by 5, and fizz buzz for numbers divisible by 3 and 5. The lowest multiple of 3 and 5 is 15, so fizz buzz is essentially for numbers divisible by 15.

In this modification, rather than strings, the count for each in a sequence is desired, meaning the ouput should be an array containing three values: only numbers divisible by 3, only numbers divisible by 5, and numbers divisible by 15. The first thought is using iteration, going over every number from 1 to n, and determining whether it’s a multiple and incrementing the count.

function solution(number){
  const mults = [0, 0, 0]
  for (let i = 1; i < number; ++i)
    if (i % 3 === 0 && i % 5 === 0)
      mults[2]++
    else if (i % 3 === 0)
      mults[0]++
    else if (i % 5 === 0)
      mults[1]++
  return mults
}

However, it doesn’t make sense to iterate over every number. All we need are the multiples of 3 and 5, and to determine if the multiple is a multiple of 15 as well. Still using iteration though, the last condition is superfluous. While iterating over multiples of 3, if it’s also divisible by 5, increment the count of multiples of 15. To avoid doubling in the second loop, since we’ll have already gone through all the multiples of 3 from 1 to n, if it’s divisible by 3, skip it.

function solution(number){
  const mults = [0, 0, 0]
  for (let i = 3; i < number; i += 3)
    if (i % 5 === 0)
      mults[2]++
    else
      mults[0]++
  for (let i = 5; i < number; i += 5)
    if (i % 3 === 0)
      continue;
    else
      mults[1]++
  return mults
}

Iteration isn’t necessary for this though. The number of multiples of a given number is the end number divided by that given number. So the number of the multiples of 15 is the integer, n / 15, then multiples of 3 and 5 are the same, except the number of the multiples of 15 have to be subtracted, to avoid doubling. As per a condition in the problem, it’s all the numbers up to n, but not including n.

function solution(number) {
  let mults_15 = Math.floor(number / 15)
  if (number % 15 === 0)
    mults_15--
  let mults_5 = Math.floor(number / 5) - mults_15
  if (number % 5 === 0)
    mults_5--
  let mults_3 = Math.floor(number / 3) - mults_15
  if (number % 3 === 0)
    mults_3--
  return [mults_3, mults_5, mults_15]
}

It’d be easier to just subtract 1 from the start, so as not to worry about decrementing by 1 after the operation.

Submission using Iteration

function solution(number) {
  const mults = [0, 0, 0]
  for (let i = 1; i < number; ++i) {
    switch (true) {
        case !(i % 15):
          mults[2]++
          break;
        case !(i % 5):
          mults[1]++
          break;
        case !(i % 3):
          mults[0]++
    }
  }
  return mults
}

For the sake of it, I chose to use a switch statement. In terms of performance, it’s the same as using if-else statements. Generally, switch is optimized to be faster, but it’s not for evaluating expressions.

When it comes to iteration, separating between 3 and 5 would be faster than checking every number. For instance, if n = 3.000.000, rather than 3.000.000 iterations, running the separate loops would be 1.600.000 iterations total. Of course with larger and larger numbers, iterations being at least O(n), a solution using calculations is preferable, making it O(1).

Sometimes, I see the use of continue. There’s no need for continue here, since the checks are performed every iteration, and if none apply, it goes to the next iteration of the loop where one would place the continue unless one were doing an initial check, to cut out of the checks sooner. While cutting out of a loop early can actually take more time than just letting the loop run—in some cases, not sure if this applies in JS though, and so I don’t know about the use of continue, what affect it has on loops.

Using if-else:

function solution(number) {
    const mults = [0, 0, 0]
    for (let i = 0; i < number; i += 3)
        if (!(i % 15))
            mults[2]++
        else
            mults[0]++
    for (let i = 0; i < number; i += 5)
        if ((i % 15) && !(i % 5))
            mults[1]++
    return mults
}

Submission using Calculation

function solution(number){
  number--
  const common = Math.floor(number / 15)
  return [Math.floor(number / 3) - common, 
          Math.floor(number / 5) - common, 
          common
  ]
}

Here, the number given is decremented by 1 to fulfill the condition of up to but not including the number. Since the counts for multiples 3 and 5 depend on multiples of 15, subtracting them, the multiples of 15 will have to be pre-calculated. There’s no need to declare extra variables to hold the counts of the multiples of 3 and 5, but it would help with clarity.

Alternative:

function solution(number) {
    number--
    const common = Math.floor(number / 15)
    const mults_3 = Math.floor(number / 3)
    const mults_5 = Math.floor(number / 5)
    return [mults_3 - common, mults_5 - common, common]
}

This is only preference though.

Fizz Buzz Cuckoo Clock

Kyu: 7
Go to description

As another variant on Fizz Buzz, the input here is time on a 24h clock and in addition to “fizz” for multiples of 3 in the minutes, “buzz” for multiples of 5, and “fizz buzz” for multiples of 3 and 5, if on the hour, a string of cuckoo for each hour should be returned, and if at half hour, just “cuckoo.” Last, if the time does not fulfill any of the conditions, the output is “tick”

Based on the conditions, the only time the hour is needed is if the minutes equate to 0. Just keeping the code from the last problem, it would be simple enough to check for being on the hour and if at half hour. For creating the string of cuckoo for each hour, there’s two ways to do it. I chose the way that only needs one line of code. Create an array with a length the number of hours, fill each slot with the word, and then join them with a space. Note that 0 equates to 12, and getting the number of the hour, in 12h format, modulo 12 would cause 12 and 24 to be 0.

function fizzBuzzCuckooClock(time) {
  const [hour, min] = time.split(":").map(t => Number(t))
  if (min === 0)
    return new Array(hour % 12 || 12)
           .fill("Cuckoo")
           .join(" ")
  if (min === 30)
    return "Cuckoo"
  switch (true) {
      case !(min % 15):
        return "Fizz Buzz"
      case !(min % 5):
        return "Buzz"
      case !(min % 3):
        return "Fizz"
      default:
        return "tick"
  }
}

Utilizing Conversion:

function fizzBuzzCuckooClock(time) {
    const [hour, min] = time.split(":")
    if (min == 0)
        return new Array(hour % 12 || 12).fill("Cuckoo").join(" ")
    if (min == 30)
        return "Cuckoo"
    if (!(min % 15))
        return "Fizz Buzz"
    if (!(min % 3))
        return "Fizz"
    if (!(min % 5))
        return "Buzz"
    return "tick"
}

Given my note on conversion, there’s no need for the conversion in the first line, changing the strings to numbers. The first two if-statements need only use equality, not a strict equality, and the conversion within the statements of the latter three conditions does not require equality.

function fizzBuzzCuckooClock(time) {
  const [hour, min] = time.split(":")
  if (min == 0) {
    let n = Number(hour) % 12 || 12
    let str = ""
    while (n > 0) {
      str += "Cuckoo "
      n--
    }
    return str.slice(0, str.length - 1)
  }
  if (min == 30)
    return "Cuckoo"
  if (min % 15 == 0)
    return "Fizz Buzz"
  if (min % 3 == 0)
    return "Fizz"
  if (min % 5 == 0)
    return "Buzz"
  return "tick"
}

Just using if-statements, since every if-statement has a return, it may be easier for another to understand at a glance. Here, I used string concatenation, which personally, I don’t like. The steps are clear, but it makes no less sense than new Array(hour % 12 || 12).fill(“Cuckoo”).join(“ “). This is more straightforward to me. The if-statements are clearer, I suppose. I prefer !(min % 15) to min % 15 == 0. Using modulo converts the min variable to a number, so there’s no need for the use of equality in the last three if-statements.

Perhaps using equality provides flexibility if the type is wrong…?

Bouncing Balls

Kyu: 6
Go to description

A bouncing ball falls to the ground h=0 a distance of h and bounces up a portion of the previous height determined by height * bounce. In this problem, we’re not counting how many times the ball bounces, but how many times it passes a point between its initial height and the ground, being the window, which has a constant height of 1.5. When the ball is above this height, it’s counted, whether falling or rising. The problem statement gives four initial checks: height is neither below 0 nor less than or equal to the height of the window, and bounce should be a value between 0 and 1. When initially solving a problem, knowing the checks means getting them out of the way. In this case, return -1.

After the checks, it’s guaranteed that the height is above the window. It will always fall first, and since the height is above the window, there will always be a count of 1. Since the count is unknown, a for-loop would not be appropriate, and given the assumption that the ball falls then bounces up, presuming the height to start the iteration is above the window, this always means an increment of 1 to the count. There’s no need to determine between above and below the window, which was my original thought. Calculating the next height means the ball will rise to that height, therefore determining if the ball will pass the window on the way up. If not, the count isn’t incremented and on the next iteration, the height is below the window, so there’s no more counting. while (h > window) n++ is the ball falling, h = h * bounce; if (h > window) n++ is the ball rising.

function bouncingBall(h,  bounce,  window) {
  if (h < 0 || bounce <= 0 || bounce >= 1 || window >= h)
    return -1
  let n = 0
  while (h > window) {
    n++
    h = h * bounce
    if (h > window)
      n++
  }
  return n
}

Rather than keeping a count, pushing in the heights to an array and then taking the length would be the same. However, given the falling and rising, the height at an index is the height the ball drops, and so between indices is the ball rising to the next height. This means that the number of times the ball passes the window is the length + (length - 1). Since the while loop handles the condition h > window, there’s little need to do this check twice. The loop wouldn’t even run if h <= window, making the length of the heights array 0. if (!heights.length) return -1 addresses this issue. Not to mention, whether h is less than 0 is the same as h being less than window, since the height of the window is constant. The only check required at the start is the bounce as 0 would break the loop, 1 would be an infinite loop, and less than 0 would result in negative heights every odd number of times.

function bouncingBall(h,  bounce,  window) {
  if (bounce >= 1 || bounce <= 0)
    return -1
  const heights = []
  while (h > window) {
    heights.push(h)
    h = h * bounce
  }
  if (!heights.length)
    return -1
  return heights.length + (heights.length - 1)
}

It is cleaner though to place the h <= window check at the start. Letting it be handled by the condition in the while loop is more work than needed, since if the check fails, there’s no use continuing. Also, this removes the need for the if-statement about lengths.

function bouncingBall(h,  bounce,  window) {
  if (bounce <= 0 || bounce >= 1 || h <= window)
    return -1
  let n = 0
  while (h > window) {
    n++
    h *= bounce
  }
  return n + (n - 1)
}

Is My Friend Cheating?

Kyu: 5
Go to description

Honestly, I’m not sure the game the friend is cheating at, but that’s irrelevant. My first attempts were all at solving this problem using a more iterative approach. By that, I mean having one variable a for a lower-bound number and b for an upper-bound number, then moving them based on meeting certain conditions, in other words, using a flexible sliding window. I even thought I had to, since my first exposure to this problem was at the monthly event, Code Katas. While I finally came up with a solution, I have no idea if it worked because the server timed out.

So I gave up on that and went looking for an algebraic solution instead. The sum was easy as the formula for calculating the sum of a sequence of numbers I’ve used numerous times before. From the problem, I was left with a * b = sum - (a + b). I’m not a math person, so I didn’t know just to add a +1 to ab + a + b = sum.

  1. ab + a + b + 1 = sum + 1

  2. a(b + 1) + (b + 1) = sum + 1

  3. (a + 1)(b + 1) = sum + 1

  4. solve for a: a = (sum + 1) / (b + 1) - 1

There would still be iteration, and I figured the answer was at least above half of a number. The test case was n = 26, ans: [[15, 21], [21, 15]]. The one thing I remembered from the event was starting from the last number, but instead of two variables, presuming that the calculated number would always be lower than my input, until the calculated number was no longer lower than the input, meaning they’ve crossed, I’d just be decrementing the higher number.

Once I had the algebra, the thing to give me a headache was reversing the array, so [[15, 21]] would become [[15, 21], [21, 15]].

const removeNb = (n) => {
    const sum = Math.floor(n / 2 * (n + 1))
    const solve = (b) => Math.floor((sum + 1) / (b + 1) - 1)
    const check = (a, b) => a * b === sum - (a + b)

    const sol = []
    let b = n, a = solve(b)
    while (a < b) {
        if (check(a, b))
            sol.push([a, b])
        b--
        a = solve(b)
    }
    return sol.concat(sol.map(([a, b]) => [b,a]).reverse())
}

Performance-wise, seeing other solutions, the last line may actually tax it more than just iterating from n/2 to n.

Clever Solutions

Loop-only: I separated out the formulas, but the sum really is only needed to calculate the second factor. At the same time, I understand why I didn’t do this. For one, my code is decrementing and assumes the calculated factor is less than the first factor acting as an upper bound. I also used Math.floor, instead of checking whether b is an integer and less than n. The only thing I envy is not having the reverse the array at the end, but even at n=1.000.003, the end array only had four pairs.

const removeNb = (n) => {
    const res = []
    for (let a = 1; a <= n; a++) {
        let b = (n * (n + 1) / 2 - a) / (a + 1)
        if (b % 1 === 0 && b <= n)
            res.push([a, b])
    }
    return res
}
0
Subscribe to my newsletter

Read articles from Stella Marie directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Stella Marie
Stella Marie

Interests: Frontend D&D Abstract designs Storytelling in games and writing BS Informatics, specialization: User Experience design [USA] MA Design, specialization: Interaction design [China]