{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This is an ipython (jupyter) worksheet with code from the lecture notes for the course\n",
"**Permutation Puzzles: A Mathematical Perspective**, by Jamie Mulholland\n",
"\n",
"Coures webpage: http://www.sfu.ca/~jtmulhol/math302
\n",
"Course notes booklet: http://www.sfu.ca/~jtmulhol/math302/notes/302notes.pdf"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Lecture 24: Lights Out"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 24.2 Matrix Model\n",
"\n",
"## 24.2.2 Solving Linear Systems in SageMath"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here is an example of using SageMath to solve the linear system:\n",
"$$\n",
"\\begin{bmatrix}\n",
"1 & 0 & 2\\\\\n",
"3 & 2 & 5\n",
"\\end{bmatrix}\n",
"\\boldsymbol{x} = \n",
"\\left[\\begin{array}{c}\n",
"3\\\\\n",
"0 \\\\\n",
"\\end{array}\n",
"\\right]\n",
"$$"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(3, -9/2, 0)"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"M=matrix(QQ,[[1,0,2],[3,2,5]])\n",
"b=vector(QQ,[3,0])\n",
"M.solve_right(b)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Lights Out Matrix"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# Definition of the matrix for Lights Out\n",
"# input = integer n (where lights out board is nxn)\n",
"# output = lights out matrix A which is nxn\n",
"def lights_out(n):\n",
" A = identity_matrix(GF(2),n*n) # initialize A with ones along diagonal\n",
" for i in range(n):\n",
" for j in range(n):\n",
" m = n*i+j \n",
" if i > 0 : A[(m,m-n)] = 1 # I block below diagonal\n",
" if i < n-1 : A[(m,m+n)] = 1 # I block above diagonal\n",
" if j > 0 : A[(m,m-1)] = 1 # C block below diagonal\n",
" if j < n-1 : A[(m,m+1)] = 1 # C block above diagonal\n",
" return A"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[1 1 0 1 0 0 0 0 0]\n",
"[1 1 1 0 1 0 0 0 0]\n",
"[0 1 1 0 0 1 0 0 0]\n",
"[1 0 0 1 1 0 1 0 0]\n",
"[0 1 0 1 1 1 0 1 0]\n",
"[0 0 1 0 1 1 0 0 1]\n",
"[0 0 0 1 0 0 1 1 0]\n",
"[0 0 0 0 1 0 1 1 1]\n",
"[0 0 0 0 0 1 0 1 1]"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"lights_out(3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The lights out matrix for a 5x5 boaard would be a 25x25 matrix. SageMath won't print this out by default."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"25 x 25 dense matrix over Finite Field of size 2 (use the '.str()' method to see the entries)"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"lights_out(5)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Example 24.1"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"starting configuration: \n",
"[1 1 0 0 1]\n",
"[1 1 1 0 0]\n",
"[1 0 0 0 1]\n",
"[0 0 1 1 1]\n",
"[1 0 0 1 1]\n",
"solution: \n"
]
},
{
"data": {
"text/plain": [
"[0 0 1 0 0]\n",
"[1 0 1 1 1]\n",
"[0 1 0 1 0]\n",
"[1 1 1 0 1]\n",
"[0 0 1 0 0]"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#current game configuration (i.e. buttons that are lit) \n",
"b=vector(GF(2),[1,1,0,0,1, 1,1,1,0,0, 1,0,0,0,1, 0,0,1,1,1, 1,0,0,1,1]);\n",
"print \"starting configuration: \"\n",
"print matrix(GF(2),5,5,b.list())\n",
"\n",
"#solving the game\n",
"x=lights_out(5).solve_right(b);\n",
"\n",
"#now put the solution x in a nice matrix form so we can see what buttons to press\n",
"button_press_matrix = matrix(GF(2),5,5,x.list()) # convert vector to a list then a matrix\n",
"print \"solution: \"\n",
"button_press_matrix "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Lights Out Solver Function (basic version)"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# Definition of the solution function for Lights Out\n",
"# input = integer n (where lights out baord is nxn), and b is the configuration vector\n",
"# output = a matrix x indicating which buttons to press for a solution\n",
"def lights_out_solver(n,b):\n",
" x=lights_out(n).solve_right(b);\n",
" button_press_matrix = matrix(GF(2),n,n,x.list())\n",
" return button_press_matrix"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[0 0 1 0 0]\n",
"[1 0 1 1 1]\n",
"[0 1 0 1 0]\n",
"[1 1 1 0 1]\n",
"[0 0 1 0 0]"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"b=vector(GF(2), [1,1,0,0,1, 1,1,1,0,0, 1,0,0,0,1, 0,0,1,1,1, 1,0,0,1,1])\n",
"lights_out_solver(5,b)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 24.2.3 Solvable Configuration"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A lit button configuration $\\boldsymbol{b}$ is solvable if the corresponding linear system $A\\boldsymbol{x}= \\boldsymbol{b}$ has a solution. \n",
"From linear algebra we know\n",
"\n",
"$$A\\boldsymbol{x}= \\boldsymbol{b} \\text{ is solvable for every } \\boldsymbol{b} \\quad \\iff \\quad \\text{$A$ is invertible} \\quad \\iff \\quad \\det(A)\\ne 0.$$\n",
"\n",
"The lights out matrix (for $5\\times 5$ game) has determinant $0$. Therefore, there do exist unsolvable configurations $\\boldsymbol{b}$."
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"0"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"lights_out(5).determinant()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Example of an unsolvable configuration and how SageMath will let you know with a traceback error."
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {
"collapsed": false
},
"outputs": [
{
"ename": "ValueError",
"evalue": "matrix equation has no solutions",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mValueError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[0mb\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mvector\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mGF\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m2\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mlights_out_solver\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m5\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mb\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;32m\u001b[0m in \u001b[0;36mlights_out_solver\u001b[1;34m(n, b)\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[1;31m# output = a matrix x indicating which buttons to press for a solution\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 4\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mlights_out_solver\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mn\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mb\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 5\u001b[1;33m \u001b[0mx\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mlights_out\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mn\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msolve_right\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mb\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m;\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 6\u001b[0m \u001b[0mbutton_press_matrix\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mmatrix\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mGF\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mInteger\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m2\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mn\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mn\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mlist\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 7\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mbutton_press_matrix\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;32m/opt/sage/src/sage/matrix/matrix2.pyx\u001b[0m in \u001b[0;36msage.matrix.matrix2.Matrix.solve_right (/opt/sage/src/build/cythonized/sage/matrix/matrix2.c:7149)\u001b[1;34m()\u001b[0m\n\u001b[0;32m 401\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 402\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mrank\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;33m!=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mnrows\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 403\u001b[1;33m \u001b[0mX\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_solve_right_general\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mC\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcheck\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mcheck\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 404\u001b[0m \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 405\u001b[0m \u001b[0mX\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_solve_right_nonsingular_square\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mC\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcheck_rank\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mFalse\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;32m/opt/sage/src/sage/matrix/matrix2.pyx\u001b[0m in \u001b[0;36msage.matrix.matrix2.Matrix._solve_right_general (/opt/sage/src/build/cythonized/sage/matrix/matrix2.c:8296)\u001b[1;34m()\u001b[0m\n\u001b[0;32m 519\u001b[0m \u001b[1;31m# Have to check that we actually solved the equation.\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 520\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0mX\u001b[0m \u001b[1;33m!=\u001b[0m \u001b[0mB\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 521\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"matrix equation has no solutions\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 522\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mX\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 523\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;31mValueError\u001b[0m: matrix equation has no solutions"
]
}
],
"source": [
"b=vector(GF(2),[1,0,1,0,1, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0])\n",
"lights_out_solver(5,b)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Could use try and except for error proofing."
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Current state has no solution.\n"
]
}
],
"source": [
"b=vector(GF(2),[1,0,1,0,1, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0])\n",
"try:\n",
" lights_out_solver(5,b)\n",
"except:\n",
" print \"Current state has no solution.\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The set of solvable configurations is the column space of the lights out matrix $A$, $\\text{col}(A)=\\text{span}(\\boldsymbol{t}_{1,1}, \\boldsymbol{t}_{1,2},\\ldots,\\boldsymbol{t}_{5,5})$, and the dimension of $\\text{col}(A)$ is called the rank of $A$, denoted $\\text{rank}(A)$."
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"23"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"lights_out(5).rank()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Therefore only $23$ buttons are required to solve any configuration, and if each one can either be pressed or not, then there are $2^{23}$ solvable configurations, out of a possible $2^{25}$ configurations. Therefore the probability that a random configuration is solvable is 1/4."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Quite Patterns (Null Space of Lights Out Matrix)\n",
"\n",
"There exist sequences of button presses that will leave the lights unchanged. These are known as **quiet patterns**. Such a sequence **x** is a solution to the homogeneous equation $A\\boldsymbol{x}= \\boldsymbol{0}$. That is, **x** is in the null space of $A$, denoted by $\\text{nul}(A)$. Since $A$ has rank 23 then it has nullity 2 and so $\\text{nul}(A)$ contains 4 vectors:\n",
"\n",
"\\begin{align*}\n",
"\\text{nul}(A)& = \\text{span}(\\boldsymbol{d}_1,\\boldsymbol{d}_2) = \\{ r_1\\boldsymbol{d}_1+r_2\\boldsymbol{d}_2 \\ |\\ r_1, r_2 \\in \\mathbb{F}_2\\}\\\\\n",
" &=\\{ \\boldsymbol{0}, \\boldsymbol{d}_1, \\boldsymbol{d}_2, \\boldsymbol{d}_1+ \\boldsymbol{d}_2 \\}.\n",
" \\end{align*}\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"Vector space of degree 25 and dimension 2 over Finite Field of size 2\n",
"Basis matrix:\n",
"[1 0 1 0 1 1 0 1 0 1 0 0 0 0 0 1 0 1 0 1 1 0 1 0 1]\n",
"[0 1 1 1 0 1 0 1 0 1 1 1 0 1 1 1 0 1 0 1 0 1 1 1 0]"
]
},
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"lights_out(5).right_kernel()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If $\\boldsymbol{s_1}$ and $\\boldsymbol{s_2}$ are both solutions of a configuration $\\boldsymbol{b}$ then $A\\boldsymbol{s_1}=A\\boldsymbol{s_2}=b$ so $\\boldsymbol{s_1}-\\boldsymbol{s_2}$ is in the null space of A.\n",
"\n",
"Therefore, all solutions for $\\boldsymbol{b}$ are $\\boldsymbol{s_1}+\\text{nul}(A)$:\n",
"\n",
"$$\\{ \\boldsymbol{s_1}, \\boldsymbol{s_1}+\\boldsymbol{d}_1, \\boldsymbol{s_1}+\\boldsymbol{d}_2, \\boldsymbol{s_1}+\\boldsymbol{d}_1+ \\boldsymbol{d}_2 \\}$$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"\n",
"## 24.2.4 Optimal Solution to Lights Out"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"First we define a function which counts the number of 1's in a vector."
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# Function: number_of_presses\n",
"# input = a vector x of dimension 25 with 0,1 entries\n",
"# output = the number of times 1 appears as an entry\n",
"def number_of_presses(x):\n",
" counter=0; # initialize counter, which is our variable to count 1's\n",
" for i in range(0,25): # recall python indexes lists from 0, not 1\n",
" if x[i]==1: counter=counter+1 # check if the ith entry is 1, increment counter\n",
" return counter"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"3"
]
},
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"b=vector(GF(2),[1,0,1,0,1, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0])\n",
"number_of_presses(b)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Could also do it by converting the vector from GF(2) to $\\mathbb{Q}$ then summing entries."
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"3"
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"b=vector(GF(2),[1,0,1,0,1, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0])\n",
"bQ = vector(QQ,b.list()) # change field to rationals\n",
"sum(bQ)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we'll find all 4 solutions to Example 24.1."
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0) 12\n",
"(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1) 8\n",
"(0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0) 8\n",
"(1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1) 20\n"
]
}
],
"source": [
"b=vector(GF(2), [1,1,0,0,1, 1,1,1,0,0, 1,0,0,0,1, 0,0,1,1,1, 1,0,0,1,1])\n",
"x=lights_out(5).solve_right(b) # one solution\n",
"nulsp = lights_out(5).right_kernel()\n",
"for d in nulsp:\n",
" print x+d, number_of_presses(x+d)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"An optimal solution is (1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1), which contains 8 presses."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 24.3 Summary of Lights Out (5x5)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[1 0 1 0 1]\n",
"[1 0 1 0 1]\n",
"[0 0 0 0 0]\n",
"[0 0 0 0 0]\n",
"[0 0 0 0 0]"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Definition of the matrix for Lights Out\n",
"# input = integer n (where lights out board is nxn)\n",
"# output = lights out matrix A which is n^2 x n^2\n",
"def lights_out(n):\n",
" A = identity_matrix(GF(2),n*n) # initialize A with ones along diagonal\n",
" for i in range(n):\n",
" for j in range(n):\n",
" m = n*i+j \n",
" if i > 0 : A[(m,m-n)] = 1 # I block below diagonal\n",
" if i < n-1 : A[(m,m+n)] = 1 # I block above diagonal\n",
" if j > 0 : A[(m,m-1)] = 1 # C block below diagonal\n",
" if j < n-1 : A[(m,m+1)] = 1 # C block above diagonal\n",
" return A\n",
"\n",
"\n",
"# Function: number_of_presses\n",
"# input = a vector x of dimension 25 with 0,1 entries\n",
"# output = the number of times 1 appears as an entry\n",
"def number_of_presses(x):\n",
" counter=0;\n",
" for i in range(0,25):\n",
" if x[i]==1: counter=counter+1\n",
" return counter\n",
"\n",
"# Function: optimal_solution\n",
"# input = a strategy vector x\n",
"# output = an equivalent strategy vector which uses the minimum number of button presses\n",
"def optimal_solution(x):\n",
" op_button_presses=x\n",
" n=number_of_presses(x)\n",
" nul=lights_out(5).right_kernel()\n",
" for d in nul:\n",
" if number_of_presses(x+d)