What you're trying to do is find the vector in the solution space with the minimum Hamming weight. Unfortunately, this problem is equivalent to the minimum distance problem in coding theory, which has been proven to be NP-hard (http://people.math.umass.edu/~hajir/m499c/vardy-complexity.pdf). That said, the implementation you link to can be sped up greatly by using ints as bit arrays and using bitwise operations, effectively parallelizing many of the operations.
Here's the quick-and-dirty Java solution I wrote for the Google Foobar challenge, which runs reasonably quickly and works for n <= 15.
public class GridZero
{
public static int[][] returnn3()
{
final int[][] n3={{284}, {149}, {78}, {63}};
return n3;
}
public static int[][] returnn5()
{
final int[][] n5= {{17284592}, {8659223}, {4329627}, {2164829}, {1082430}, {1015839}, {31775}, {1023}};
return n5;
}
public static int[][] returnn7()
{
final int[][] n7={{-2130838537, 122816}, {1082196484, 4191}, {541098242, 2159}, {270549121, 1143}, {135274560, 66171}, {67637280, 33149}, {33818640, 16638}, {33292288, 127}, {260096, 127}, {2032, 127}, {15, 114815}, {0, 16383}};
return n7;
}
public static int[][] returnn9()
{
final int[][] n9={{-2143297553, -134480386, 130816}, {1075843080, 67240192,
65919}, {537921540, 33620096, 33215}, {268960770, 16810048,
16863}, {134480385, 8405024, 8687}, {67240192, -2143281136,
4599}, {33620096, 1075843080, 2555}, {16810048, 537921540,
1533}, {8405024, 268960770, 1022}, {8372224, 0, 511}, {16352, 0,
511}, {31, -268435456, 511}, {0, 267911168, 511}, {0, 523264,
511}, {0, 1022, 511}, {0, 1, 131071}};
return n9;
}
public static int[][] returnn11()
{
final int[][] n11= {{-2146435585, -1074266369, -537133185, 31456256}, {1074266368,
537133184, 268566592, 1050111}, {537133184, 268566592, 134283296,
526079}, {268566592, 134283296, 67141648, 264063}, {134283296,
67141648, 33570824, 133055}, {67141648, 33570824, 16785412,
67551}, {33570824, 16785412, 8392706, 34799}, {16785412, 8392706,
4196353, 18423}, {8392706, 4196353, 2098176, 16787451}, {4196353,
2098176, -2146434560, 8394749}, {2098176, -2146434560, 1074266368,
4198398}, {2096128, 0, 0, 2047}, {1023, -2147483648, 0, 2047}, {0,
2146435072, 0, 2047}, {0, 1048064, 0, 2047}, {0, 511, -1073741824,
2047}, {0, 0, 1073217536, 2047}, {0, 0, 524032, 2047}, {0, 0, 255,
29362175}, {0, 0, 0, 4194303}};
return n11;
}
public static int[][] returnn13()
{
final int[][] n13={{-2147221537, -16779265, -1073872913, -8389633, -536936456,
0}, {1073872912, 8389632, 536936456, 4194816, 268468235,
511}, {536936456, 4194816, 268468228, 2097408, 134234125,
511}, {268468228, 2097408, 134234114, 1048704, 67117070,
511}, {134234114, 1048704, 67117057, 524352, 33558543,
255}, {67117057, 524352, 33558528, -2147221472, 16779279,
383}, {33558528, -2147221472, 16779264, 1073872912, 8389647,
447}, {16779264, 1073872912, 8389632, 536936456, 4194831,
479}, {8389632, 536936456, 4194816, 268468228, 2097423,
495}, {4194816, 268468228, 2097408, 134234114, 1048719,
503}, {2097408, 134234114, 1048704, 67117057, 524367,
507}, {1048704, 67117057, 524352, 33558528, -2147221457,
509}, {524352, 33558528, -2147221472, 16779264, 1073872927,
510}, {524224, 0, 0, 0, 15, 511}, {63, -33554432, 0, 0, 15,
511}, {0, 33550336, 0, 0, 15, 511}, {0, 4095, -2147483648, 0, 15,
511}, {0, 0, 2147221504, 0, 15, 511}, {0, 0, 262112, 0, 15,
511}, {0, 0, 31, -16777216, 15, 511}, {0, 0, 0, 16775168, 15,
511}, {0, 0, 0, 2047, -1073741809, 511}, {0, 0, 0, 0, 1073610767,
511}, {0, 0, 0, 0, 131071, 511}};
return n13;
}
public static int[][] returnn15()
{
final int[][] n15={{-2147418115, -262153, -1048609, -4194433, -16777729, -67110913,
-268443648, 0}, {1073774593, 131076, 524304, 2097216, 8388864,
33555456, 134230015, 1}, {536887296, -2147418110, 262152, 1048608,
4194432, 16777728, 67123199, 1}, {268443648, 1073774593, 131076,
524304, 2097216, 8388864, 33569791, 1}, {134221824,
536887296, -2147418110, 262152, 1048608, 4194432, 16793087,
1}, {67110912, 268443648, 1073774593, 131076, 524304, 2097216,
8404735, 1}, {33555456, 134221824, 536887296, -2147418110, 262152,
1048608, 4210559, 1}, {16777728, 67110912, 268443648, 1073774593,
131076, 524304, 2113471, 1}, {8388864, 33555456, 134221824,
536887296, -2147418110, 262152, 1064927, 1}, {4194432, 16777728,
67110912, 268443648, 1073774593, 131076, 540655, 1}, {2097216,
8388864, 33555456, 134221824, 536887296, -2147418110, 278519,
1}, {1048608, 4194432, 16777728, 67110912, 268443648, 1073774593,
147451, 1}, {524304, 2097216, 8388864, 33555456, 134221824,
536887296, -2147401731, 1}, {262152, 1048608, 4194432, 16777728,
67110912, 268443648, 1073790974, 1}, {131076, 524304, 2097216,
8388864, 33555456, 134221824, 536903679, 0}, {131068, 0, 0, 0, 0, 0,
16383, 1}, {3, -524288, 0, 0, 0, 0, 16383, 1}, {0, 524272, 0, 0, 0,
0, 16383, 1}, {0, 15, -2097152, 0, 0, 0, 16383, 1}, {0, 0, 2097088,
0, 0, 0, 16383, 1}, {0, 0, 63, -8388608, 0, 0, 16383, 1}, {0, 0, 0,
8388352, 0, 0, 16383, 1}, {0, 0, 0, 255, -33554432, 0, 16383,
1}, {0, 0, 0, 0, 33553408, 0, 16383, 1}, {0, 0, 0, 0,
1023, -134217728, 16383, 1}, {0, 0, 0, 0, 0, 134213632, 16383,
1}, {0, 0, 0, 0, 0, 4095, -536854529, 1}, {0, 0, 0, 0, 0, 0,
536870911, 1}};
return n15;
}
public static int answer(int[][] matrix)
{
int[][] tm = toggleGen(matrix.length,matrix);
int[][] mat = rref(tm);
int zeroRow = mat.length;
for (int i = mat.length - 1; i >=0; i--)
{
int status = 0;
for (int j = 0; j < mat[0].length - 1; j++)
{
if (mat[i][j] == 1)
{
status = 1;
break;
}
}
if (status == 1)
{
zeroRow = i+1;
break;
}
else if (mat[i][mat[0].length-1] == 0) continue;
else return -1;
}
int offset = 0;
int changecheck = 0;
int zeroRowcopy = zeroRow;
for (int r = 0; r < zeroRowcopy; r++)
{
if (mat[r][r+offset] == 1) continue;
else
{
changecheck = 1;
mat[zeroRow++][r + offset] = 1;
offset++;
}
}
if (changecheck == 1) mat = rref(mat);
int len = mat.length/32 + 1;
if (mat.length % 32 == 0) len--;
final int[] sol = new int[len];
int weight = 0;
for (int i = 0; i < mat.length; i++)
{
int val = mat[i][mat[0].length-1];
sol[i/32] = (sol[i/32] << 1) | val;
weight += val;
}
if (matrix.length % 2 == 0) return weight;
final int[][] kernel;
switch (matrix.length)
{
case 3: kernel = returnn3();
break;
case 5: kernel = returnn5();
break;
case 7: kernel = returnn7();
break;
case 9: kernel = returnn9();
break;
case 11: kernel = returnn11();
break;
case 13: kernel = returnn13();
break;
case 15: kernel = returnn15();
break;
default:
kernel = returnn3();
}
int max = 1 << (2*(matrix.length-1));
int minweight = weight;
int res[] = new int[sol.length];
for (int i = 1; i < max; i++)
{
int hamming = basisweighter(kernel,i,sol,res);
if (hamming < minweight)
{
minweight = hamming;
}
}
return minweight;
}
public static int basisweighter(final int[][] kernel, int weights, final int[] sol, int[] res)
{
int changes = weights ^ (weights-1);
int changeint;
int i = 0;
while ((changeint = (changes >> i)) > 0)
{
if ((changeint & 1) == 1)
{
for (int k = 0; k < res.length; k++)
{
res[k] ^= kernel[i][k];
}
}
i++;
}
int weight = 0;
for (int j = 0; j < sol.length; j++)
{
weight += NumberOfSetBits(res[j]^sol[j]);
}
return weight;
}
public static int NumberOfSetBits(int i)
{
i = i - ((i >>> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
return (((i + (i >>> 4)) & 0x0F0F0F0F) * 0x01010101) >>> 24;
}
public static int[][] toggleGen(int n, int[][] mat)
{
int[][] ret = new int[n*n][n*n+1];
for (int i = 0; i < n*n; i++)
{
int[] row = new int[n*n+1];
for (int j = 0; j <= n*n; j++)
{
if (j == n*n)
row[j] = mat[i/n][i%n];
else if (i/n == j/n)
row[j] = 1;
else if (i%n == j%n)
row[j] = 1;
else
row[j] = 0;
}
ret[i] = row;
}
return ret;
}
public static int[][] rref(int[][] mat)
{
int[][] rref = new int[mat.length][mat[0].length];
int startRow = 0;
for (int r = 0; r < rref.length; ++r)
{
for (int c = 0; c < rref[r].length; ++c)
{
rref[r][c] = mat[r][c];
}
}
for (int c = 0; c < rref[0].length; ++c)
{
int pivotRow = -1;
for (int r = startRow; r < rref.length; ++r)
{
if (rref[r][c] == 1)
{
pivotRow = r;
break;
}
}
if (pivotRow == -1) continue;
if (pivotRow != startRow)
{
int[] temp = rref[pivotRow];
rref[pivotRow] = rref[startRow];
rref[startRow] = temp;
}
for (int r = 0; r < rref.length; r++)
{
if (r == startRow) continue;
if(rref[r][c] == 1)
{
for (int i = 0; i < rref[0].length; i++)
{
rref[r][i] ^= rref[startRow][i];
}
}
}
startRow++;
}
return rref;
}
}
Best Answer
First, put your rules into a matrix:
Then, you can do something called "row reduction" modulo 2. This involves adding copies of some rows to other rows (where $1+1=0$) and possibly switching some rows around. A set of rules will be solvable from any initial state exactly when the row reduced matrix has a single 1 in each row and column. If we row reduce the above matrix we get:
We can see that there are states from which the game is not solvable. In fact, because there are three rows of zeroes, there are three conditions any game state that can reach the lights out position must satisfy.
You can get these equations by calculating the null space of the matrix, which is given by the three rows of this matrix:
If we let $L_i$ be $1$ if it is on and $0$ if it is off, then, unpacking the first row, we see that $L_4+L_7+L_8+L_9+L_{11}+L_{15}+L_{16} \mod 2$ does not change under any of your above moves. This means that you can never go between the state of no lights turned on to the state of say, just $L_4$ turned on, or indeed any odd combination of $L_4,L_7,L_8,L_9,L_{11},L_{16}.$
In particular, to answer your question of whether you can go from the all lights on state to the all lights off state, since $L_4+L_7+L_8+L_9+L_{11}+L_{15}+L_{16}$ evaluates to $7=1\mod 2$ for all lights on, this is impossible.
The other two rows of the null space matrix give two other sums of lights that don't change (up to parity) under your moves.
Interestingly, if you look at the row reduced matrix, the $1$ in the upper left has only zeroes in its row. That means that you can toggle the first light in any game position using a combination of the legal moves.