Finding all legal moves in a simple board game - python

I have an 8x8 board represented by a numpy.ndarray:
array([[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0]])
# 0 = free space
# 1 = player1's figure
A figure can either move forward and left, forward and right or just forward (forward means down the board in this case).
Right now I am using nested for loops in order to look through the board indexes. When I find a figure I append the board states that can be achieved by making moves with that figure to a list and then keep searching for figures.
For this example the output of my function looks like this:
[array([[[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0]],
[[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0]],
[[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0]]])]
Is there a faster way in which I can find all the possible moves for a given board state?

Wouldn't it be easier for memory as well as performance sake, to rather than keeping a complete board in the memory , keep the player figure's position in the memory. Lets take your example, player figure's location is -
player1 = (1, 4)
Let's assume , players position is denoted by (x,y) . Then you can compute the moves for that player at runtime (no need to keep in memory) , the possible moves would be -
(x+1,y)
(x+1,y+1)
(x+1,y-1)
If the player figure can circle back in the board, that is if he is at the bottom most position in the board, and then next moves would be the top most row , if that is the case, the moves would be determined by taking their modulo against the number of rows and columns (assuming nr - number of rows and nc - number of columns ) . Example , for (x,y) next moves would be -
((x+1) % nr,y % nc)
((x+1) % nr,(y+1) % nc)
((x+1) % nr,(y-1) % nc)

Related

Printing a 2D game board

I have to make an othello game board and I was wondering how you make it so it prints the board properly. This is my code at the moment.
import collections
NONE = 0
BLACK = 'B'
WHITE = 'W'
BOARD_COLUMNS = int(input('How many board columns? '))
BOARD_ROWS = int(input('How many board rows? '))
class OthelloGameState:
def _new_game_board() -> [[int]]:
board = []
for col in range(BOARD_COLUMNS):
board.append([])
for row in range(BOARD_ROWS):
board[-1].append(NONE)
return board
print (_new_game_board())
I used the print to see what the board looks like and it came out like:
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
How would I make it so it's supposed to be the way it's supposed to be?
import pprint
board = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
pprint.pprint(board)
Outputs:
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
Check out a full run of the above:
https://ideone.com/6buCwM
You might want to make your board a state variable of the OthelloGameState class.
That way you can update that and your _new_game_board() method will just fill that with the proper size of rows and columns.
Then you can use Copy and Paste's answer to print the board (or your own method if you want to display it without the braces).
def print_board(board):
for col in range(BOARD_COLUMNS):
print(' '.join([row for row in range(BOARD_ROWS)]))
Python's print() method will not format your output at all, so you will need to do that yourself.
https://ideone.com/bNxew0 for a working example

In python solve for a matrix with restrictions

Here's the problem: I have two vectors A (1Xn) and B (1Xm) where n>m. I'm looking for a matrix T (nXm), such that AT=B. T has the following properties: All elements of T are either 1's or 0's. The elements in each row in T sum to 1. Ideally, I would like the program to return the best solution where as many elements of AT-B=0 if there is not a perfect solution.
Here's an example:
import numpy as np
A = np.array([-1.051, 1.069, 0.132, -0.003, -0.001, 0.066, -0.28,
-0.121, 0.075, 0.006, 0.229, -0.018, -0.213, -0.11])
B = np.array([-1.051, 1.201, -0.003, -0.001, 0.066, -0.121, 0.075,
-0.045,-0.231, -0.11])
T = np.array([[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1]])
# This should equal a vector of 0's
print A.dot(T)-B
I've come up with something, but I don't think it's totally satisfactory. I'd prefer not to have to go this route as it's clunky. I first try 1 to 1 mapping because that's a common solution to many of the mappings. Anything left over I move to iterating over all possibilities. This gets messy a bit quickly. You'll also notice I'm fairly new to numpy. I'd appreciate any feedback on how to improve this. Thanks.
def solver(B,A):
m=B.size
n=A.size
start=np.ones((1,n))
start=np.concatenate((start,np.zeros((m-1,n))),axis=0).astype(np.int)
for i in xrange(0,m):
T=np.roll(start,i,axis=0)
test=B.dot(T)-A
if i==0:
matches=np.absolute(test)<.0001
else:
matches=np.vstack((matches,np.absolute(test)<.0001))
rA=(A-B.dot(matches))[np.absolute(A-B.dot(matches))>.0001]
Amissing=A-B.dot(matches)
rB=(B-B*np.sum(matches,axis=1))[np.absolute(B-B*np.sum(matches,axis=1))>.0001]
Bmissing=B-B*np.sum(matches,axis=1)
rm=rB.size
rn=rA.size
rmxrn = np.arange(rm*rn).reshape(rm,rn)
dif=np.absolute(rA)
best=np.zeros(shape=(rm,rn))
for i in xrange(0, 2**(rm*rn)):
arr = (i >> rmxrn) % 2
if np.amax(np.sum(arr,axis=1))>1 or np.sum(arr)>rm:
continue
else:
diftemp=rB.dot(arr)-rA
besttemp=arr
if np.sum(np.absolute(diftemp))<np.sum(np.absolute(dif)):
dif=diftemp
best=besttemp
if np.sum(np.absolute(dif)<.0001)==rn:
break
best=best.astype(np.bool)
matchesT=matches.T
bestT=best.T
newbestT=np.zeros(shape=(m,rn)).astype(np.bool).T
for x in xrange(0,rn):
counter=0
for i, value in enumerate(Bmissing):
if abs(Bmissing[i])>.0001:
newbestT[x,i]=bestT[x,counter]
counter=counter+1
for x in xrange(0,rn):
counter=0
for i, value in enumerate(Amissing):
if abs(Amissing[i])>.0001:
matchesT[i]=newbestT[counter]
counter=counter+1
return(matchesT.T)
A=np.array([-1.051,1.201,-0.003,-0.001,0.066,-0.121,0.075,-0.045,-0.231,-0.11])
B=np.array([-1.051,1.069,0.132,-0.003,-0.001,0.066,-0.28,-0.121,0.075,0.006,0.229,-0.018,-0.213,-0.11])
print solver(B,A)

How can I further optimize this solver of a variant of "Lights Out"?

I'm still solving this problem, taken from the current "Google Foobar" challenge. It's a variation of the "Lights Out" game, in which pressing a light will flip the state of every light on the same row and the same column.
I previously tried using a BFS, which turned out to be too slow for n > 6, while I need to handle 2 < n < 16. I currently have a program that can handle all even n and all odd numbers except 13 and 15. Here's what it does:
I use the strategy outlined by #Aryabhata to find a special solution x' of some system Ax = b that can be associated with an instance of this problem (see here for details).
Having found a base of the null space of A, I compute all sums of x' plus a linear combination of the vectors of the base.
The set of those sums is the set of all solutions of the original problem, therefore I can find by brute-force the solution that achieves the minimum.
It should be noted that, for n even, the null space is empty (A is invertible), therefore x' achieves the minimum because it's the only solution. If n is odd the number of vectors in a base of the null space is 2n - 2, therefore the search space has size 2^(2n - 2), which is 2^28 in the worst case (n = 15).
Here's my program:
from itertools import product
MEMO = {}
def bits(iterable):
bit = 1
res = 0
for elem in iterable:
if elem:
res |= bit
bit <<= 1
return res
def mask(current, n):
if (current, n) in MEMO:
return MEMO[(current, n)]
result = 0
if current < n:
for j in xrange(n):
result += (2 ** ((current - 1)*n + j) + 2 ** (current*n + j))
else:
for i in xrange(n):
result += (2 ** (i*n + current - n) + 2 ** (i*n + current - n + 1))
MEMO[(current, n)] = result
return result
# See: https://math.stackexchange.com/a/441697/4471
def check(matrix, n):
parities = [sum(row) % 2 for row in matrix]
for i in xrange(n):
parities.append(sum([row[i] for row in matrix]) % 2)
return len(set(parities)) == 1
def minimize(matrix, current, n):
if current == 0:
# See: https://stackoverflow.com/a/9831671/374865
return bin(matrix).count("1")
else:
return min(minimize(matrix ^ mask(current, n), current - 1, n),
minimize(matrix, current - 1, n))
def solve(matrix, n):
result = [0 for i in xrange(n) for j in xrange(n)]
for i, j in product(xrange(n), repeat=2):
if matrix[i][j]:
for k in xrange(n):
result[i*n + k] ^= 1
result[k*n + j] ^= 1
result[i*n + j] ^= 1
if n % 2 == 0:
return sum(result)
else:
return minimize(bits(result), 2*n - 2, n)
def answer(matrix):
n = len(matrix)
if n % 2 == 0:
return solve(matrix, n)
else:
if check(matrix, n):
return solve(matrix, n)
else:
return -1
I've already tried optimizing it: for instance, matrices are encoded as binary numbers by the function bits, while the function mask creates binary masks that are used to add a single element of the base to x'. Those masks are also memoized because they are frequently used, so that they are calculated only once.
The number of ones is then counted using the idiom bin(n).count('1'), which should be the fastest implementation (I checked it against the classical one by Kernighan).
So, what else can I do to squeeze more performance out of my program? Here are a few test cases:
print answer([
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]), 1
print answer([
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0]
]), 14
print answer([
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
]), 15
print answer([
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]), 14
print answer([
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
]), 15
EDIT: I passed this round. This implementation solves correctly 4 out of 5 test cases, then I brute-forced the fifth. I'm still interested in further optimizations or different algorithms!
EDIT 2: This answer, and in particular this paper give a proof that this particular problem is NP-hard (Section 3), which hints that we shouldn't be looking for a polynomial algorithm. So the question becomes: "What is the best exponent we can get?".
I tried everything about linear algebra, and since it is GF2, I do not think I could find the polynomial solution. Since the maximum number is 15, I further optimised it to approximately 2^15.
For even number
So, for n is even, there is a quicker way than standard linear algebra. If you have for example something like this,
0000
0100
0000
0000
The one solution should be (flip the row and column of the point exactly n times)
0100
1111
0100
0100
If you think about it, if you have a point which you want to flip, you can flip every point of the row and column once. (If that make sense), so it is easy to find one particular solution.
If I have something like this
0100
0010
0010
0000
one solution could be
1131
1221
1221
0120
and since flipping twice makes no difference, the solution can be reduced to
1111
1001
1001
0100
Then odd number
If n is odd, I can think of nothing but search. However, we can expand the n -> n+1 such that the solution to the problem should not contain flipping points of last row and last column.
If you have something 3x3 like:
010
001
001
you can always try expand solution to something like:
010x
001x
001x
xxxx
First, you will determine all the points in 3 by 3,
1111
1001 + ?
1001
0100
where ? should be solution to
000x
000x
000x
xxxx
As you can see, no matter how to flip, there is no way you can satisfy unless the xxx are the same bits. And you can try all the combination of the bottom to flip, and then you can determine the right hand side flipping or not by determine whether flipping results minimum number of 1 of the row.
I am really bad at explaining things, hope it will be clear enough.
I want to echo that darwinsenior's answer is very helpful! However, it took me a very long time to figure it out, even after reading that answer several times.
So, if you're late to foobar, like me, but want to get through this one without resorting to Java, here's a hint that might help.
The following light pattern isn't solvable, which I think is what confused me.
010
001
001
Here's a non-trivial example to demonstrate darwinsenior's idea:
Say you want to solve this (N=5)
11010
01000
11100
10011
00010
We know this is solvable because the parity of all sums and columns is odd.
And if N were even, it would be easier to find the answer.
So, expand to N=6 as follows:
110100
010000
111000
100110
000100
000000
Like darwinsenior said, we want a solution to this that doesn't touch any lights in the bottom row or right-most column. Then we could take that solution, ignore the bottom row and right column and we'd have a solution to the original N=5 problem. So, we want to plug in values (not just zeros) but not have any button pushes in those columns in your answer.
This means you can't put a 1 in the bottom right. A light in the bottom right would mean at least 1 button pushed in the bottom row or right-most column. So that's one "degree of freedom" gone. Next, for the bottom row to have no button pushes, all those lights must have an even parity. Look to the answer to even N case to see why. So in the case above the parity is odd. We can fill the bottom row, but we must use an odd number of 1's. This removes another "degree of freedom". If we plug in 4 values (either 1s or 0s) then the 5th value is determined by this parity requirement. So, N-1 degrees of freedom here.
This is where the brute force part comes in. I had to try all possible values here (in this case all sets of 5 bits with odd parity)
One example is to plug in 10101
110100
010000
111000
100110
000100
10101_
Now we can use the rule for even N and get a solution.
I'll write down the actual sum of row and column for each point, even though just the parity is needed in order to make it clearer what I did.
65555o 01111o
53343o 11101o
65465o -> 01001o
66554o 00110o
54333o 10111o
66464_ 00000_
I put little o's on the far right to say that the parity is odd, and because we haven't done anything with those yet. Because the parity is odd, this is no good, we would have a solution with all these being touched. But they all have odd parity, so we just need to plug in values such that the parity of the right-most column is odd, so the parity at each point is even (if that makes sense)
This is what darwinsenior said in this comment above (but I had a tough time following) The only requirement is that the parity of the column is odd and therefore no buttons on far right need to be pushed in the solution.
We don't need to brute force this, we can use some logic to figure out which buttons to push while maintaining the parity requirement. By the way, we have N-1 free variables here, so 2*(N-1) free variables, just as in other solutions mentioned. Just that here we can see the effect of our choices on the button push count. I'll choose these values for the column: 11001
Now the example is:
110101 X00000
010001 000X00
111000 -- again use even N solution -> 0X00X0
100110 00XX00
000101 0X0000
10101_ 000000
So, I think that gives us an answer to the original N=5 case (just remove the zeros at bottom and at right). It has 7 button pushes, which I think is the best we can do with this one, but I'm not sure.
One more thing- even with this big reduction in the number of cases that need to be brute forced, I still had to do what Eugene said and use a list of ints, not a list of list of ints. Look to Jacopo's code and the "bits" function for that. Fun stuff.
So I think you shouldn't need to brute force the odd case at all. My linear isn't too strong, but in R^n, if you want to find the shortest x satisfying Ax=b (which is essentially what we're doing), after finding some special solution x' you can project onto the nullspace of A and subtract the projection from x'. I believe this method should work even in F_2, though I'm not sure; please correct me if I'm wrong.
I like how #rustonian clarified the top answer on here, but there is one assumption that he took that I believe to be wrong, and that assumption is that the bottom right most bit of the added column and row can not be 1. It in fact can be 1 so that it may change all of the other added bits to 0. Here is an example of what I mean:
011 0110 4434 0010
110 -> 1100 -> 3444 -> 1000
101 1011 4644 0000
0101 4443 0001
So it seems that the bottom right bit can be 1 iff it used to turn off all other added bits. This will not take away from the 3x3 solution since the toggling the bottom right added bit does not effect the original 3x3 space.

What is the difference between [[0 for _ in range(10)] for _ in range(10)] and [0 for _ in range(10)] * 10? [duplicate]

This question already has answers here:
Nested List Indices [duplicate]
(2 answers)
Closed 9 years ago.
>>> CM = [[0 for _ in range(10)]] * 10
>>> CM
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
>>> CM[0][0] = CM[0][0] + 1
>>> CM
[[1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
I was trying to create a confusion matrix. It basically contains count of the (i, j) pairs.
I first created a list of lists, and then incremented the appropriate variable. However, it didn't work as expected. CM[i][0] got incremented for all values of i.
I found a work around.
>>> CM = [[0 for _ in range(10)] for _ in range(10)]
>>> CM
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
>>> CM[0][0] = CM[0][0] + 1
>>> CM
[[1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
But I would be grateful if someone could explain why the first method failed.
>>> CM = [[0 for _ in range(10)]] * 10
Is copying a reference to the same object, ten times. It is equivalent to this:
>>> x = [0 for _ in range(10)]
>>> CM = [x, x, x, x, x, x, x, x, x, x]
So manipulating one element causes side effects. Your workaround is elegant and correct.
Note:
This occurs since the elements of the lists are lists (which are mutable). If they were strings for example, which are immutable, it wouldn't be an issue if the same string was referenced in different lists, since they can't be manipulated. Python doesn't like to waste memory (unless explicitly told to ie. deepcopy), so copying lists will simply copy their references.

Python list confusion [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 1 year ago.
Let's say I have the following code:
a_list = [[0]*10]*10
This generates the following list:
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
Then I want to modify the first element in the first list:
a_list[0][0] = 23
I expected only the first element of the list to be modified, but actually the first element of each list was changed:
[[23, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
I managed to find another way to represent my data to avoid this but why is this happening? Why isn't just the first list changed? When I do the second *10, does Python actually copy the first list's address instead of allocating a new memory block?
Your hunch about copying addresses is correct. Think about it like this:
sub_list = [0] * 10
a_list = [sub_list] * 10
This code is actually equivalent to the code you have posted above. What this means is that you are actually changing the same list sub_list whenever you change any element of a_list. You can even make sure of it by typing:
a_list = [[0] * 10] * 10
for n in a_list:
print id(n)
And it will show up the same for every element. To remedy this, you should use:
a_list = [[0] * 10 for _ in range(10)]
In order to create a new sublist for every element of a_list.
Lists contain references to objects. Multiplication on lists simply repeats the references (to the same objects!). While this is fine for immutable objects (like integers), what you are getting is multiple references to the same list.
Create separate lists with this pattern [[0]*10 for _ in xrange(10)].
Why isn't just the first list changed?
The reason is simple, there really is only 1 list, not 10 - just as you already suspected:
In [1]: [[0]*10]*10
Out[1]:
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
In [2]: map(id, _)
Out[2]:
[54094624,
54094624,
54094624,
54094624,
54094624,
54094624,
54094624,
54094624,
54094624,
54094624]
If you want to create 10 lists, you can achieve this easily via an expression like
[[0]*10 for x in xrange(10)]

Categories