The "Circlular Spatula" Puzzle
Problem
Try out the puzzle here (with parameters ), and view the source code here.
It's a variant of the Google Code Jam “oversized spatula” puzzle. The goal of the puzzle is to turn on all lights. You can press a light, which toggles the neighboring lights.
You can consider the puzzle with parameters , where is the number of lights and clicking toggles lights at a time
I wasn’t able to find any other analysis. Maybe this is also a famous puzzle which has already been solved; in which case, I'm re-inventing the wheel.
Solution
The solution when is odd is just to click all the lights once.
It works because all the actions commute. This is easy to see, since if you click two lights in a different order, the number of toggles for each light will be the same. So since is odd, each light will be toggled times and end up in the on state.
Minimality
The main question this post asks is, given parameters , does the solution described above use the minimal number of steps? (This question was asked by a friend I showed this to.)
Computational Approach
It's easy to see that the solution above is minimal if (the only way to enable all the lights is to click each one). Since , all possible states are reachable. If all states are reachable, then each state has a unique set of clicks that results in it. So, if all states are reachable, there is only one solution (and it is therefore minimal).
Which does the puzzle have the same number of reachable states as the puzzle does for ?
from itertools import product
from fractions import gcd
def flipK(arr, i, k):
l = len(arr)
for j in xrange(i, i+k): arr[j%l] += 1
def express(arr, k):
""" returns expression of the action [arr] in the puzzle """
out = [0 for _ in xrange(len(arr))]
for i, a in enumerate(arr):
if a: flipK(out, i, k)
return tuple([o % 2 for o in out])
def is_same(n, k):
expressions = [express(action, k) for action in product([0, 1], repeat=n)]
num_total = len(expressions) # states for (n, 1)
num_uniq = len(set(expressions)) # states for (n, k)
return num_total == num_uniq
solutions = []
for n in xrange(2, 18):
for k in xrange(1, n+1, 2):
if is_same(n, k):
solutions.append((n, k))
print solutions
"""
Output (n, k):
[(2, 1), (3, 1), (4, 1), (4, 3), (5, 1), (5, 3), (6, 1), (6, 5), (7, 1), (7, 3), (7, 5), (8, 1), (8, 3), (8, 5), (8, 7), (9, 1), (9, 5), (9, 7), (10, 1), (10, 3), (10, 7), (10, 9), (11, 1), (11, 3), (11, 5), (11, 7), (11, 9), (12, 1), (12, 5), (12, 7), (12, 11), (13, 1), (13, 3), (13, 5), (13, 7), (13, 9), (13, 11), (14, 1), (14, 3), (14, 5), (14, 9), (14, 11), (14, 13), (15, 1), (15, 7), (15, 11), (15, 13), (16, 1), (16, 3), (16, 5), (16, 7), (16, 9), (16, 11), (16, 13), (16, 15), (17, 1), (17, 3), (17, 5), (17, 7), (17, 9), (17, 11), (17, 13), (17, 15)]
"""
We can see by inspection that for , the result is true when is odd and , where represents .
We'll now show that this is true in general if is odd and . In other words, when .
Analysis
Claim
For parameters where , the action of clicking all the buttons (which we'll call ) is the unique solution.
Notation
We'll use -tuples to represent actions (i.e., the action of clicking the first, third, etc. lights in any order). Multiple actions can be composed by writing .
We'll define the "simple" action . In other words, is the action of clicking the th light.
We'll use to denote the state resulting from applying the action to the all-off state, under the parameters .
And we'll let be the set of possible states in the puzzle with parameters .
If we prove the following equivalent claim, then the claim follows from the counting argument mentioned above.
Equivalent Claim
If , then .
Proof
We’re going to flip exactly one light (that is, explicitly construct the state ) using actions on .
We'll do this by flipping adjacent ranges of lights until we get one light. Consider the state and consider as a sequence in .
Notice that for any , then is the number of lights in the enabled state.
Therefore, the end state has only one light flipped if and only if .The key insight is that since , then is a generator for , and there is always some where .
So, for this value of , we have . Notice that we can shift this action clockwise by any amount, and construct the state .
Since we have constructed the action of pressing every button under , and we can compose these "simple" actions, then by closure we can conclude that .
Also, every state in can be constructed in by simply clicking the lights that are active in the state from . Therefore, .
Additional Comments
For some , the puzzle is actually not solvable – for example, . I wasn’t able to work out in general when the puzzle is solvable if , but I would be interested in playing around with this in the future.