Taro Logo

Bulb Switcher

Medium
7 years ago

You are given n bulbs, initially all off. You perform n rounds of switches. In the first round, you turn on all the bulbs. In the second round, you turn off every second bulb. In the third round, you toggle every third bulb (turning on if it's off or turning off if it's on). You repeat this process for n rounds. Your task is to write a function that, given the number of bulbs n, returns the number of bulbs that are on after n rounds.

Here's a breakdown of the process and some examples to illustrate the problem:

  • Initial State: All n bulbs are off.
  • Round 1: Turn on all bulbs.
  • Round 2: Toggle bulbs 2, 4, 6, 8, ...
  • Round 3: Toggle bulbs 3, 6, 9, 12, ...
  • Round i: Toggle bulbs i, 2i, 3i, 4i, ...
  • Final State: Determine how many bulbs are on after n rounds.

Here are some examples:

  • Example 1: n = 3

    • Initially: [off, off, off]
    • After round 1: [on, on, on]
    • After round 2: [on, off, on]
    • After round 3: [on, off, off]
    • Answer: 1
  • Example 2: n = 0

    • Answer: 0
  • Example 3: n = 1

    • Initially: [off]
    • After round 1: [on]
    • Answer: 1
  • Example 4: n = 6

    • The bulbs that are on are 1 and 4
    • Answer: 2

Can you provide an efficient algorithm to solve this problem? Explain your reasoning and the time complexity of your solution.

Sample Answer

Bulb Switcher Problem

Let's explore the "Bulb Switcher" problem. The prompt states: There are n bulbs that are initially off. You first turn on all the bulbs. Then, you turn off every second bulb. On the third round, you toggle every third bulb (turning on if it's off or turning off if it's on). For the ith round, you toggle every ith bulb. For the nth round, you only toggle the last bulb. Return the number of bulbs that are on after n rounds.

Naive Solution

A straightforward approach is to simulate the entire process. We can use a boolean array to represent the state of each bulb. We iterate through each round, toggling the appropriate bulbs.

python def bulb_switcher_naive(n): bulbs = [False] * n # Initialize all bulbs to off (False)

for i in range(1, n + 1):
    for j in range(i - 1, n, i):
        bulbs[j] = not bulbs[j]  # Toggle the bulb

return sum(bulbs)  # Count the number of on bulbs (True)

Big(O) Run-time: O(n^2), due to the nested loops. Big(O) Space Usage: O(n), to store the boolean array representing the bulbs.

Optimal Solution

The key insight here is that a bulb's final state (on or off) depends on how many times it's toggled. A bulb is toggled once for each of its factors. For example, bulb number 12 is toggled by rounds 1, 2, 3, 4, 6, and 12.

If a number has an odd number of factors, the bulb will be on. If a number has an even number of factors, the bulb will be off.

Most numbers have an even number of factors because factors usually come in pairs. For example, the factors of 12 are (1, 12), (2, 6), and (3, 4).

However, perfect squares have an odd number of factors. For example, the factors of 9 are (1, 9) and (3, 3). The factor 3 is only counted once.

Therefore, the number of bulbs that are on is equal to the number of perfect squares less than or equal to n.

python import math

def bulb_switcher_optimal(n): return int(math.sqrt(n))

Big(O) Run-time: O(1), as math.sqrt takes constant time. Big(O) Space Usage: O(1), as we are using a constant amount of extra space.

Edge Cases

  • n = 0: The optimal solution correctly returns 0, as there are no perfect squares less than or equal to 0.
  • n = 1: The optimal solution returns 1, as 1 is a perfect square.
  • Large n: The math.sqrt function can handle large values of n without integer overflow problems (within the limits of floating-point precision).

Explanation

The optimal solution avoids simulating the entire process, resulting in significant time savings. By recognizing that only perfect squares will be on, we can directly calculate the result using the square root function.