I came up with a programming problem that goes something like this:
suppose you have a NxN binary grid. “Tapping” a square toggles it and all edge-adjacent squares. Find the shortest sequence of “taps” that converts the grid to all 1s.
The easiest to see algorithm is a BFS, where we start at a grid of all 1s, and repeatedly visit each board that is one “tap” away from the current board, while making sure that we never visit a board twice. However, since there are 2^(n^2) board states, it’s a pretty inefficient algorithm.
I was able to find a polynomial time (O(n^2)) algorithm for N <= 3, but it breaks down for N>=4, since it relies on the fact that each board is solvable.
Is there a polynomial time algorithm for N>=4?
Thanks!
Edit: Here is an outline of my algorithm for n=3 (also applies for smaller N):
- Tapping is an xor of the current board with a “plus” of 1s.
- Tapping the same cell twice is the same as not tapping it at all, since an xor a = 0.
- Tap order doesn’t matter, since xor is associative. Thus, we can represent a set of taps as a NxN grid, just like a board.
Let A->B denote tapping B onto board A Let 1 denote the NxN grid of all 1s.
Note that 1->(1->(1->(1->(A)))) = A for all boards A with N<=3. To my knowledge, there’s no intuitive proof of this; you just have to expand it out using xor and it eventually resolves.
Let B = 1->(1->(1->A)). By substitution, 1->B = A, so 1->B->B = A->B = 1, by (2) above, so we have found B as desired.
Since every board is reachable, and there are the same number of board states as tap sets, then B is unique, so it is optimal.
It should work with BFS. A propper BFS algorithm does not rely on the fact that there is a solution: when there is none, it just falls through its main loop without results.
Some observations:
It is never necessary to tap the same cell more than once. If you tapped it twice, it is the same as not tapping it. It is a XOR operation.
The order of the taps is not important. The effect of tapping the same cells in a different order is the same.
You can improve a bit on BFS by implementing the A* search algorithm. For the realised cost you could count 5 per performed tap (to represent the maximum number of flipped cells), and for an admissible heuristic you could count the number of cells that are still 0. Note that an A* search uses a priority queue.
Working with bit representations and using bit operations like AND, OR and XOR, may speed up an implementation, although it doesn't influence the time complexity.
When you have a 0 cell in the matrix, it is certain that this cell or any of its edge-neighbors needs to be tapped. So it makes sense to select one (only) of the zeroes and consider the 5 taps (at most) as "next" moves.
Here is an implementation in Python with some comments to explain details:
Here is an example run on a 10x10 matrix:
The
tapsmatrix will represent the cells that need to be tapped (in any order):As the heuristic function is admissible, the returned solution is guaranteed to represent the minimum of taps needed.