Creating sublist from a give list of items - python

I would say first that the following question is not for homework purpose even because i've finish software engineer a few months ago. Anyway today I was working and one friend ask to me this strange sorting problem.
"I have a List with 1000 rows, each row represent a number, and I want to create 10 sub lists each have a similar summation of the numbers from the main list. How can I do that?"
For example I've the main list composed by 5,4,3,2 and 1. It's simple, I create two sub lists
one with 5 and 3 the other with 4,2 and 1 the result of each list it's similar: 8 for the first 7 for the second.
I can't figure it out the algorithm even if know it's simple but I'm missing something.

Let A be the input array. I'll assume it is sorted ascending.
A = [2,3,6,8,11]
Let M[i] be the number of sublist found so far to have sum equal to i.
Starts with only M[0] = 1 because there is one list with has sum equals zero, that is the empty list.
M = [1,0,0,...]
Then take each item from the list A one-by-one.
Update the number of ways you have to compose a list of each sum when considering
that the item you just take can be used.
Suppose a is the new item
for each j:
if M[j] != 0:
M_next[j+a] = M[j+a] + M[j]
When you found any M[j] which reach 10 during that, you should stop the algorithm.
Also, modify to remember the items in the list to be able to get the actual list at the end!
Notes:
You can use sparse representation for M
This is similar to those Knapsack and subset sum problems.
Perhaps you might find many better algorithms reading on those.
Here is a working code in Python:
A = [2,3,6,8,11]
t = sum(A)
M = [0]*(t+1)
M[0] = 1
print 'init M :',M
for a in A:
for j in range(len(M)-1,-1,-1):
if M[j] != 0:
M[j+a] += M[j]
print 'use',a,':',M
And its output:
init M : [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]
use 2 : [1, 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]
use 3 : [1, 0, 1, 1, 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]
use 6 : [1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
use 8 : [1, 0, 1, 1, 0, 1, 1, 0, 2, 1, 1, 2, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
use 11 : [1, 0, 1, 1, 0, 1, 1, 0, 2, 1, 1, 3, 0, 2, 2, 0, 2, 2, 0, 3, 1, 1, 2, 0, 1, 1, 0, 1, 1, 0, 1]
Take the interpretation of M[11] = 3 at the end for example;
it means there are 3 sublists with sum equals 11.
If you trace the progress, you can see the sublists are {2,3,6},{3,8},{11}.
To account for the fact that you allow the 10 sublists to have similar sum. Not just exactly the same sum. You might want to change termination condition from "terminate if any M[j] >= 10" to "terminate if sum(M[j:j+3]) >= 10" or something like that.

Related

Performing bitwise operations on binary strings made from arrays

Basically I'm trying to grab 1's and 0's values from an array and perform bitwise operations on that.
board = np.array([1, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
for x in board:
s += str(x)
s = int(s)
This is obviously not correct however. This has its own binary value and if I perform bitwise operations on it (eg. >>) I'm shifting the underlying binary.
So how can I dynamically create binary strings to perform bitwise operations on?
Any help appreciated.
board = np.array([1, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
for x in board:
s += str(x)
s = int(s)
int automatically converts to base 10 but you could also use int(s,2) to convert it to base 2. You'd then get the base 10 representation of your base2 number. So int("110",2) would be 6 and 6<<2 would be 24 or 6>>2 would be 1.
Also in terms of making the stringyfication easier yon can use
s = "".join(map(str, board))

Using List Comprehension for a number sequence (1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1 .....) in Python

I'm doing exercise questions from A Practical Introduction to Python Programming by Brian Heinold (pg 83) and there was a simpler question:
Using a for loop, create the list below, which consists of ones separated by increasingly many
zeroes. The last two ones in the list should be separated by ten zeroes.
[1,1,0,1,0,0,1,0,0,0,1,0,0,0,0,1,....]
# Question 11
L = [1]
for i in range(11):
if i == 0:
L.append(1)
else:
for j in range(i):
L.append(0)
L.append(1)
print(L)
Use a list comprehension to create the list below, which consists of ones separated by increasingly many zeroes. The last two ones in the list should be separated by ten zeroes.
[1,1,0,1,0,0,1,0,0,0,1,0,0,0,0,1,....]
I'm having difficulty with converting it into one line using list comprehension. Best I could do was
# Question 15
L = [[1, [0]*i] for i in range(11)]
print(L)
L = [j for row in L for j in row]
print(L)
Which would print:
[[1, []], [1, [0]], [1, [0, 0]], [1, [0, 0, 0]], [1, [0, 0, 0, 0]], [1, [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, 0]], [1, [0, 0, 0, 0, 0, 0, 0, 0, 0]], [1, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]]
[1, [], 1, [0], 1, [0, 0], 1, [0, 0, 0], 1, [0, 0, 0, 0], 1, [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, 0], 1, [0, 0, 0, 0, 0, 0, 0, 0, 0], 1, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
Flattening the list any further would result in a TypeError: 'int' object is not iterable and I would have to add another 1 at the end of the list using a seperate command which I guess would be fine. To add the code for flattening the list kinda goes over my head.
This is a second attempt using if/else statements, that gives the produces the same output and gives the same TypeError for when I try to flatten it completely.
L = [1 if i%2 == 0 else [0]*(i//2) for i in range(22)]
L = [j for row in L for j in row]
print(L)
I just want this as output:
[1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 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, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
Thanks!
You can solve it like this:
L = [j for i in range(11) for j in [1, *([0]*i)]]
or
L = [j for i in range(11) for j in [1, *(0 for k in range(i))]]
Edit
I missed that it should end with a one. Here's a solution that does that:
L = [1, *(j for i in range(11) for j in [*(0 for k in range(i)), 1])]
or with list comprehension instead of generators:
L = [1, *[j for i in range(11) for j in [*[0 for k in range(i)], 1]]]
You could use a nested comprehension that produces an increasing number of values that you convert to 0s and 1s by returning 0s for all but the last of each group:
n = 10
r = [1]+[b//z for z in range(1,n+2) for b in range(1,z+1)]
print(r)
[1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 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, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
If you want to avoid the need to add the initial [1], you could use a similar approach starting z at -1 instead of 1:
r = [ 1-(b<z) for z in range(-1,n+1) for b in range(z+1 or 1)]
I know it's not allowed in the problem, but assume we can use math library, there's a really interesting solution that goes like this:
from math import sqrt, floor
print([1] + [1 * ((-1+sqrt(1+8*i))/2 > 0 and floor((-1+sqrt(1+8*i))/2) == (-1+sqrt(1+8*i))/2 or (-1-sqrt(1+8*i))/2 > 0 and floor((-1-sqrt(1+8*i))/2) == (-1-sqrt(1+8*i))/2) for i in range(1, 67)])
The key observation is that all ones in the target list has an index that's a triangular number except for the one on index 0(no pun intended). So we can just check if the current index is a triangular number.
The nth triangular number can be expressed as:
Let i be the current index we are checking. All we need to do is to solve:
And check if one of the roots is a natural number.
Again, I know this is not a valid answer. I just want to show it as the idea is interesting.
p=[]
for i in range(11):
p.append(1)
for j in range(i):
p.append(i*0)
print(p)

How to convert this using list comprehension?

l = []
for i in range(11):
l.append(1)
for j in range(i):
l.append(0)
print(l)
The output follows a (1, 1, 0, 1, 0, 0, 1, 0, 0, 0, ...) pattern. I do not know however how to convert nested-for loops using list comprehension.
This is tricky to write as a comprehension because of the need to add two separate elements in each iteration. It can be implemented using a list sum:
l = sum([[1] + [0] * i for i in range(11)], [])
Output:
[
1,
1, 0,
1, 0, 0,
1, 0, 0, 0,
1, 0, 0, 0, 0,
1, 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, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
]
You can use the following:
l = [0 if j else 1 for i in range(11) for j in range(i+1)]
or in a slightly shorter albeit more obfuscated form:
l = [int(not j) for i in range(11) for j in range(i+1)]

numpy: check for 1 every 6 element every row

I need to have something like this:
arr = array([[1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0,
0, 0, 0, 1, 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, 1, 0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0]])
Where each row contains 36 elements, every 6 element in a row represents a hidden row, and that hidden row needs exactly one 1, and 0 everywhere else. In other words, every entry mod 6 needs exactly one 1. This is my requirement for arr.
I have a table that's going to be used to compute a "fitness" value for each row. That is, I have a
table = np.array([10, 5, 4, 6, 5, 1, 6, 4, 9, 7, 3, 2, 1, 8, 3,
6, 4, 6, 5, 3, 7, 2, 1, 4, 3, 2, 5, 6, 8, 7, 7, 6, 4, 1, 3, 2])
table = table.T
and I'm going to multiply each row of arr with table. The result of that multiplication, a 1x1 matrix, will be stored as the "fitness" value of that corresponding row. UNLESS the row does not fit the requirement described above, which should return 0.
an example of what should be returned is
result = array([5,12,13,14,20,34])
I need a way to do this but I'm too new to numpy to know how to.
(I'm Assuming you want what you've asked for in the first half).
I believe better or more elegant solutions exist, but this is what I think can do the job.
np.all(arr[:,6] == 1) and np.all(arr[:, :6] == 0) and np.all(arr[:, 7:])
Alternatively, you can construct the array (with 0's and 1's) and then just compare with it, say using not_equal.
I'm also not 100% sure of your question, but I'll try to answer with the best of my knowledge.
Since you're saying your matrix has "hidden rows", to check whether it is well formed, the easiest way seems to be to just reshape it:
# First check, returns true if all elements are either 0 or 1
np.in1d(arr, [0,1]).all()
# Second check, provided the above was True, returns True if
# each "hidden row" has exactly one 1 and other 0.
(arr.reshape(6,6,6).sum(axis=2) == 1).all()
Both checks return "True" for your arr.
Now, my understanding is that for each "large" row of 36 elements, you want a scalar product with your "table" vector, unless that "large" row has an ill-formed "hidden small" row. In this case, I'd do something like:
# The following computes the result, not checking for integrity
results = arr.dot(table)
# Now remove the results that are not well formed.
# First, compute "large" rows where at least one "small" subrow
# fails the condition.
mask = (arr.reshape(6,6,6).sum(axis=2) != 1).any(axis=1)
# And set the corresponding answer to 0
results[mask] = 0
However, running this code against your data returns as answer
array([38, 31, 24, 24, 32, 20])
which is not what you mention; did I misunderstand your requirement, or was the example based on different data?

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.

Categories