Today’s challenge was about multithreading. To be honest, I have no idea how multithreading works in Python, so I struggled to understand the question not only about finding the solution.
The title of the problem was Fizz Buzz Multithreaded
.
Problem description
You are given an instance of the class FizzBuzz that has four functions: fizz, buzz, fizzbuzz and number. The same instance of FizzBuzz will be passed to four different threads:
- Thread A: calls
fizz()
that should output the word “fizz”. - Thread B: calls
buzz()
that should output the word “buzz”. - Thread C: calls
fizzbuzz()
that should output the word “fizzbuzz”. - Thread D: calls
number()
that should only output the integers.
Modify the given class to output the series [1, 2, "fizz", 4, "buzz", ...]
where the ith
token (1-indexed) of the series is:
- “fizzbuzz” if
i
is divisible by3
and5
, - “fizz” if
i
is divisible by3
and not5
, - “buzz” if
i
is divisible by5
and not3
, or i
ifi
is not divisible by3
or5
.
Examples
Input:
n = 15
Output:
[1, 2, "fizz", 4,"buzz", "fizz", 7, 8, "fizz", "buzz", 11, "fizz", 13, 14, "fizzbuzz"]
Input:
n = 5
Output:
[1, 2, "fizz", 4, "buzz"]
You can find a brief description about the problem here.
Experience
Before writing any code, I googled how multithreading works. After I understood how it works, I tried to fool the system by storing the instance of the functions (i.e., fizz, buzz, fizzbuzz, number) on an instance variable called functions to call them later. Here is the code snippet to clarify what I mean:
def number(self, printNumber: 'Callable[[int], None]') -> None:
for number in range(1, self.n + 1):
if number % 3 != 0 and number % 5 != 0:
self.callResults[number] = printNumber
time.sleep(0.1)
self.checkAndFinish()
def checkAndFinish(self):
if len(self.callResults) == self.n:
for number, function in self.callResults.items():
if number % 3 != 0 and number % 5 != 0:
function(number)
else:
function()
All the codes are similar for the functions, the only difference is the condition. After every function is executed it will sleep for 0.1 second and call checkAndFinish function. The problem with this approach is, I can’t call the function while being inside another thread.
I spent almost 2 and a half hours and was about to give up, but in my final trial, I found some code snippets that led me to the solution.
The results
- Runtime: 40 ms, faster than 91.57% of Python3 online submissions.
- Memory Usage: 14.6 MB, less than 55.56% of Python3 online submissions.
The code
class FizzBuzz:
def __init__(self, n: int):
self.n = n
self.counter = 1
# printFizz() outputs "fizz"
def fizz(self, printFizz: 'Callable[[], None]') -> None:
i = self.counter
while i <= self.n:
if i % 3 == 0 and i % 5 != 0:
printFizz()
self.counter += 1
time.sleep(0.000001)
i = self.counter
# printBuzz() outputs "buzz"
def buzz(self, printBuzz: 'Callable[[], None]') -> None:
i = self.counter
while i <= self.n:
if i % 5 == 0 and i % 3 != 0:
printBuzz()
self.counter += 1
time.sleep(0.000001)
i = self.counter
# printFizzBuzz() outputs "fizzbuzz"
def fizzbuzz(self, printFizzBuzz: 'Callable[[], None]') -> None:
i = self.counter
while i <= self.n:
if i % 3 == 0 and i % 5 == 0:
printFizzBuzz()
self.counter += 1
time.sleep(0.000001)
i = self.counter
# printNumber(x) outputs "x", where x is an integer.
def number(self, printNumber: 'Callable[[int], None]') -> None:
i = self.counter
while i <= self.n:
if not (i % 3 == 0 or i % 5 == 0):
printNumber(i)
self.counter += 1
time.sleep(0.000001)
i = self.counter