You have a function printNumber
that can be called with an integer parameter and prints it to the console.
printNumber(7)
prints 7
to the console.You are given an instance of the class ZeroEvenOdd
that has three functions: zero
, even
, and odd
. The same instance of ZeroEvenOdd
will be passed to three different threads:
zero()
that should only output 0
's.even()
that should only output even numbers.odd()
that should only output odd numbers.Modify the given class to output the series "010203040506..."
where the length of the series must be 2n
.
Implement the ZeroEvenOdd
class:
ZeroEvenOdd(int n)
Initializes the object with the number n
that represents the numbers that should be printed.void zero(printNumber)
Calls printNumber
to output one zero.void even(printNumber)
Calls printNumber
to output one even number.void odd(printNumber)
Calls printNumber
to output one odd number.Example 1:
Input: n = 2 Output: "0102" Explanation: There are three threads being fired asynchronously. One of them calls zero(), the other calls even(), and the last one calls odd(). "0102" is the correct output.
Example 2:
Input: n = 5 Output: "0102030405"
Constraints:
1 <= n <= 1000
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
We want to print numbers in a specific order: first zero, then even, then odd, using multiple threads. The brute force method isn't typically the right fit here, but we can imagine it as exhaustively managing turns for each thread.
Here's how the algorithm would work step-by-step:
import threading
class ZeroEvenOdd:
def __init__(self, maximum_number):
self.maximum_number = maximum_number
self.current_number = 1
self.turn = 0 # 0: zero, 1: even, 2: odd
self.lock = threading.Lock()
self.zero_event = threading.Event()
self.even_event = threading.Event()
self.odd_event = threading.Event()
def zero(self, print_number):
for _ in range(self.maximum_number):
with self.lock:
self.zero_event.wait()
print_number(0)
if self.current_number % 2 == 0:
self.turn = 1 # Next turn: even
self.even_event.set()
else:
self.turn = 2 # Next turn: odd
self.odd_event.set()
self.zero_event.clear()
def even(self, print_number):
while self.current_number <= self.maximum_number:
with self.lock:
self.even_event.wait()
# Ensure that we have not exceeded max_number
if self.current_number > self.maximum_number:
self.even_event.clear()
break
print_number(self.current_number)
self.current_number += 1
self.turn = 0 # Next turn: zero
self.zero_event.set()
self.even_event.clear()
def odd(self, print_number):
while self.current_number <= self.maximum_number:
with self.lock:
self.odd_event.wait()
# Ensure that we have not exceeded max_number
if self.current_number > self.maximum_number:
self.odd_event.clear()
break
print_number(self.current_number)
self.current_number += 1
self.turn = 0 # Next turn: zero
self.zero_event.set()
self.odd_event.clear()
def start(self):
self.zero_event.set()
if __name__ == "__main__":
number = 5
zero_even_odd_instance = ZeroEvenOdd(number)
def print_number(number):
print(number, end="")
zero_thread = threading.Thread(target=zero_even_odd_instance.zero, args=(print_number,))
even_thread = threading.Thread(target=zero_even_odd_instance.even, args=(print_number,))
odd_thread = threading.Thread(target=zero_even_odd_instance.odd, args=(print_number,))
zero_thread.start()
even_thread.start()
odd_thread.start()
zero_even_odd_instance.start()
zero_thread.join()
even_thread.join()
odd_thread.join()
The goal is to coordinate multiple threads to print numbers in a specific order: zero, even, then odd. We achieve this by using synchronization mechanisms that allow each thread to wait for its turn and signal the next thread in the sequence.
Here's how the algorithm would work step-by-step:
import threading
class ZeroEvenOdd:
def __init__(self, n):
self.number = n
self.zero_turn = True
self.even_turn = False
self.odd_turn = False
self.lock = threading.Lock()
self.condition = threading.Condition(self.lock)
def zero(self, printNumber):
for i in range(self.number):
with self.condition:
while not self.zero_turn:
self.condition.wait()
printNumber(0)
self.zero_turn = False
# Alternate between even and odd turn
if (i + 1) % 2 == 0:
self.even_turn = True
else:
self.odd_turn = True
self.condition.notify_all()
def even(self, printNumber):
for i in range(2, self.number + 1, 2):
with self.condition:
while not self.even_turn:
self.condition.wait()
printNumber(i)
self.even_turn = False
# After printing even, give zero turn
self.zero_turn = True
self.condition.notify_all()
def odd(self, printNumber):
for i in range(1, self.number + 1, 2):
with self.condition:
while not self.odd_turn:
self.condition.wait()
printNumber(i)
self.odd_turn = False
# After printing odd, give zero turn
self.zero_turn = True
self.condition.notify_all()
# Example usage (can't be run without a printNumber function)
# def print_number(x):
# print(x, end='')
#
# n = 5
# zero_even_odd = ZeroEvenOdd(n)
#
# zero_thread = threading.Thread(target=zero_even_odd.zero, args=(print_number,))
# even_thread = threading.Thread(target=zero_even_odd.even, args=(print_number,))
# odd_thread = threading.Thread(target=zero_even_odd.odd, args=(print_number,))
#
# zero_thread.start()
# even_thread.start()
# odd_thread.start()
#
# zero_thread.join()
# even_thread.join()
# odd_thread.join()
Case | How to Handle |
---|---|
n is zero | The program should print only '0' once and then terminate gracefully without entering the loop. |
n is one | The program should print '01' and then terminate. |
n is negative | Treat n as zero, printing only '0' once and returning, or throw an IllegalArgumentException. |
Large n leading to potential integer overflow when calculating even/odd numbers | Use a data type (like long) that can accommodate the range of numbers or implement overflow checks. |
Thread starvation of one of the threads | Use proper synchronization mechanisms (e.g., Semaphores, Locks, Condition Variables) to ensure fair thread execution and prevent indefinite waiting. |
Spurious wakeups affecting synchronization | Use while loops with condition variable checks to handle spurious wakeups correctly. |
n is a very large number and printing becomes slow | Consider using a thread pool to print multiple numbers in parallel if output speed is critical for very large n. |
Interrupting the threads while they are waiting | Handle InterruptedExceptions gracefully, allowing the threads to exit cleanly and stop the printing process. |