How to sum columns of an adjacency matrix in O(n) time - algorithm

I have an n x n adjacency matrix for a directed graph. I want to search to see if any of the columns sum up to n. The problem is that I have to do this in O(n) time. Is there any way to approach this with O(n) time or is it not possible (no code required)?
For reference, below is the question I am trying to solve:
Problem:
During the school bandmates election, each person has a few preferences for the president and the set of preferences for a person includes him/herself all the time. The "perfect" president is the one who is in the set of preferences of every person and who does not prefer anyone but him/herself. All we want to know is whether such a person exists or not.
Define a directed graph with the set of candidates as the vertices and a directed edge from vertex a to vertex b if and only if b is in the set of preferences of a.
There are n people
We want an algorithm which executes in O(n) time
We are given the graph described above in the form of an n x n adjacency matrix
I figured that if everyone votes for the "perfect president", then he/she will have n incoming nodes, therefore summing the column should give us n. If there is a better way to approach this than the way I am doing it, any hints would be appreciated to guide me in the right direction.

Let me repeat the rules and give them numbers for easy reference:
All prefer themselves
All prefer the perfect president (if there is one)
The perfect president (if there is one) does not prefer anyone else
Let's define the adjacency matrix so that the value in row i and column j is 1 when person i has a preference for person j. All other values are 0.
The above three rules can be reformulated in terms of the adjacency matrix as follows:
The main diagonal of the adjacency matrix will have all 1's.
If the perfect president is number i, then column i will have all 1's.
If the perfect president is number i, then row i will have all 0's, except in column i.
Note that there cannot be two or more perfect presidents, as then they would have to prefer each other (rule 2), which violates rule 3.
Algorithm: Zig-Zag Phase
The perfect president (if existing) can be found in linear time, by zig-zagging from the top-left corner of the adjacency matrix (row 0, column 0), according to the following rules:
If the value is 1, then move down to the next row (same column)
If the value is 0, then move right to the next column (same row)
While staying within the boundaries of the matrix, keep repeating the first two steps.
If you exit the matrix this phase ends. Let's call the column, where we exit the matrix, column p.
Observation: because of rule 1, this part of the algorithm will never get into the values above the main diagonal: the 1-values on that diagonal are like a wall that pushes us downward whenever we bump into it. You will therefore always exit the matrix via a downward move from the last row. In the most extreme case it would be a downward move at the 1 that is in the right-bottom corner (on the diagonal) of the matrix.
Here is a simple example of an adjacency matrix where arrows indicate how the algorithm dictates a path from the top-left corner to a 1 somewhere in the bottom row:
1 1 0 1 0
↓
1 1 0 1 0
↓
0 → 1 1 1 0
↓
0 0 → 0 → 1 0
↓
0 1 1 1 1
↓
=p
Note that in this example there is a perfect president, but that is of course not a requirement: the algorithm will give you a value p whether or not there is a perfect president.
Claim: The perfect president, if there is one, has to be the person with number p.
Proof
Given is the p returned by the above zig-zag algorithm.
First we prove by contradiction that:
a) the perfect president cannot be a person with a number i less than p.
So let's assume the contrary: the perfect president is person i and i < p:
Since we start the zig-zag phase in column 0 and ended up in column p, and since we cannot skip a column during the process, there was a moment that we were in column i. Since we didn't stay in that column, it must mean that column i has a zero in one of its rows which called us to move to the right. But rule 2 demands that if i is a perfect president, column i should only have 1-values (rule 2). Contradiction!
Secondly we prove by contradiction that:
b) the perfect president cannot be a person with a number i greater than p.
So let's assume the contrary: the perfect president is person i and i > p:
Since we start the zig-zag phase in row 0 and reached the last row (cf. the observation), and since we cannot skip a row during the process, there was a moment that we were in row i. Since we didn't stay in that row, but moved down at some point (cf. the observation: we moved out of the matrix with a downward move), it must mean that row i has a 1 in one of its columns which called us to move downwards. This 1 cannot be the 1 that is on the diagonal (at [i,i]), because we did not reach column i: i is greater than p and the algorithm ended in column p. So it must have been another 1, a second one, in row i.
But rule 3 demands that if i is a perfect president, row i should only have one 1-value, with all other values being zeroes. Contradiction!
These two proofs by contradiction leave us no other possible number for the perfect president, if there is one, than number p.
Algorithm: Verification Phase
To actually check whether person p is indeed a perfect president is trivial: we can just check whether column p contains all 1's, and that row p only contains 0's, except in column i.
Time Complexity
All this can be done in linear time: at most 2n-1 reads from the matrix are required in the zig-zag phase, and 2n-2 more in the final verification phase. Thus this is O(n).

Related

Maximum cost from a set of labels 1-n each with a weight vi

I'm trying to solve a problem wherein I have a set of items with labels l1,l2,l3,....ln each of which is associated with a weight vi.
n is "EVEN".
There are 2 people each of which will take turns to pick one item at a time. You can only pick an item with the largest or smallest remaining label.
All labels l1,l2,..ln are distinct but their weights vi may not be distinct.
I'm the 1st person to start. How do I ensure that I get the max weight after all the items are picked up?
This is the Q. Now this looks like a max-min problem to me with constraints that I can only pick largest or smallest label.
For example: if we have labels l5, l6, l7, l8 then the smallest remaining label is l5 while the largest remaining label is l8. So I can pick either l5 or l8.(their values may be anything).
Can someone help me understand as to how do I proceed with this?
This can be solved by dynamic programming.
Say you create an n X n matrix, P, indicating the largest possible payoff for you. Specifically, Pi, j indicates the largest payoff available to you if the sequence is between i and j (both inclusive).
By definition, the answer to your question is P1, n.
Start by setting any entry of the form Pi, i + d for d = 1. These indicate sequences of length 2. Obviously, the payoff is the maximum of the two elements minus the minimum (recall that you are the player who starts).
Suppose you have filled out P for all entries of the form Pi, i + d', for some odd d' > 1, and now you wish to fill it up for all entries of the form *Pi, i + d' + 2. There are four choices to check:
you choose from the left and the adversary chooses from the left
you choose from the left and the adversary chooses from the right
you choose from the right and the adversary chooses from the left
you choose from the right and the adversary chooses from the right
For each of these four options, you can calculate the result as you choice minus the adversary's choice plus results previously already filled out in P. Choose the largest of them.
The complexity is Θ(n2).

Algorithmic Analysis and interpretation of partitions

So I've been working on this problem and have been stuck forever.
I don't feel like I've made any progress so any help would be great.
For i. I thought it might be the probability of being on the left of the partition multiplied by the probability of being on the right partition. So something like (q/n)* ((n-q)/n). However if I were to do this, I would get the exact same thing for iii. Which doesn't seem correct.
Am I going about this correctly?
I also am unsure how to find the expected number of elements for the other parts. What does that even mean? what equation would I make to solve this?
How are we supposed to know the expected if it could really be anything?
I know that the qth position is sorted so how would I use that to solve it?
First, assume each permutation is equally likely, and that all the elements in the array are different.
Any particular element that's originally to the left of q, is equally likely to go in any of the remaining places in the array. That means it's got probability 1 - q/n of moving to the right. Similarly, an element originally to the right of q has probability (q - 1)/n of moving to the left. Because of the way the partition has been implemented, an element never moves from left to a different place on the left, or right to a different place on the right.
Expectation is linear, and the expected number of moves of a single element is simply its probability of moving. Therefore the expected number of moves from right to left is the product of the probability of moving times the number of elements originally on the right, or (n - q)(q - 1)/n. Similarly, the expected number of moves from left to right is (q - 1)(1 - q/n). So given a particular q, the expectation is the sum of these two, which is 2(n - q)(q - 1)/n.
Now, to get the overall expected number of moves excluding the movement of the partition element, we have to sum up over q, each q being equally likely:
1/n * sum(q=0 to n-1) 2(n - q)(q - 1) / n.
Simplifying, this equals (n^2 - 3n - 4) / 3n.
Finally, we have to add the expected number of moves of the partition which is (n - 1)/n (since it moves except when it happens to be the smallest element in the array).
Simplifying again, this gives (n^2 - 2n - 5) / n.
(Assuming I didn't make an error in calculation along the way!)
Like #Anonymous, I'll assume the elements are all different and the permutations are equally likely.
Let's address the first question first and let's solve the inverse problem. That is:
i. What is the probability that no element moved from the left to the right of q.
Before finding this out, I'd like to point out that if no element moved from the left to the right, therefore no element moved from the right to the left either. This implies that the answer to questions i. and iii. are the same, and so are the answers to questions ii. and iv.. In fact, this is easy to establish by symmetry.
So, let's calculate that probability. Let's divide the possible cases by the value of q and sum up. For any given q (q starts from 1), you have q-1 numbers that are smaller than it. You also have q-1 slots on the left and n-q slots on the right. To total possible permutations of the n-1 numbers (excluding the pivot which is at position q) is (n-1)!. The number of cases where the smaller q-1 numbers are on the left, and the larger n-q numbers are on the right is (q-1)!(n-q)!. The probability of this happening is therefore:
(q-1)!(n-q)!
------------
(n-1)!
To get the total chances, we sum all possible cases divided by n (Note: what we calculated was a conditional probability. The set of possibilities can be divided by the value of q into distinct subsets whose union covers the whole set. In other words, the complete probability can be calculated as the sum of probabilities in each subset multiplied by the probability of that subset. The probability of the pivot ending in location q is 1/n).
n
1 ___ (q-1)!(n-q)!
p = --- \ ------------
n /__ (n-1)!
q=1
This was the chance that no element moves from the left to the right. The chance of having at least one element move is thus 1-p. This could also be rewritten as:
n
1 ___ (q-1)!(n-q)!
p_1 = --- \ (1 - ------------)
n /__ (n-1)!
q=1
where p_1 is the probability for question i.. Note that the term inside the sigma is indeed the chance for a given q that at least some element moved from left to right. Let's call this p_1q as the probability for question i. given a fixed q.
To answer the second question, we know that the expected value would be the sum of the expected value given a q multiplied by the chances for that q. Chances for q being any value is 1/n, as stated before. Now, what is the expected value of number of elements on the left that need to move to right given some q? This is itself the sum of different values multiplied by their probability.
Let's say given a q, there are k values on the left that need to move to the right. In other words, there are k values larger than the pivot at position q and q-k-1 values smaller than the pivot. What are these chances? You need to calculate this again with combinatorics, where k of the q-1 elements on the left are selected from the q-1 values smaller than the pivot. Unfortunately, this answer is taking up longer than I expected and I really need to get back to work (plus, typing equations without latex math is painful). Hope this would help you continue solving this problem yourself.

Hungarian algorithm matching one set to itself

I'm looking for a variation on the Hungarian algorithm (I think) that will pair N people to themselves, excluding self-pairs and reverse-pairs, where N is even.
E.g. given N0 - N6 and a matrix C of costs for each pair, how can I obtain the set of 3 lowest-cost pairs?
C = [ [ - 5 6 1 9 4 ]
[ 5 - 4 8 6 2 ]
[ 6 4 - 3 7 6 ]
[ 1 8 3 - 8 9 ]
[ 9 6 7 8 - 5 ]
[ 4 2 6 9 5 - ] ]
In this example, the resulting pairs would be:
N0, N3
N1, N4
N2, N5
Having typed this out I'm now wondering if I can just increase the cost values in the "bottom half" of the matrix... or even better, remove them.
Is there a variation of Hungarian that works on a non-square matrix?
Or, is there another algorithm that solves this variation of the problem?
Increasing the values of the bottom half can result in an incorrect solution. You can see this as the corner coordinates (in your example coordinates 0,1 and 5,6) of the upper half will always be considered to be in the minimum X pairs, where X is the size of the matrix.
My Solution for finding the minimum X pairs
Take the standard Hungarian algorithm
You can set the diagonal to a value greater than the sum of the elements in the unaltered matrix — this step may allow you to speed up your implementation, depending on how your implementation handles nulls.
1) The first step of the standard algorithm is to go through each row, and then each column, reducing each row and column individually such that the minimum of each row and column is zero. This is unchanged.
The general principle of this solution, is to mirror every subsequent step of the original algorithm around the diagonal.
2) The next step of the algorithm is to select rows and columns so that every zero is included within the selection, using the minimum number of rows and columns.
My alteration to the algorithm means that when selecting a row/column, also select the column/row mirrored around that diagonal, but count it as one row or column selection for all purposes, including counting the diagonal (which will be the intersection of these mirrored row/column selection pairs) as only being selected once.
3) The next step is to check if you have the right solution — which in the standard algorithm means checking if the number of rows and columns selected is equal to the size of the matrix — in your example if six rows and columns have been selected.
For this variation however, when calculating when to end the algorithm treat each row/column mirrored pair of selections as a single row or column selection. If you have the right solution then end the algorithm here.
4) If the number of rows and columns is less than the size of the matrix, then find the smallest unselected element, and call it k. Subtract k from all uncovered elements, and add it to all elements that are covered twice (again, counting the mirrored row/column selection as a single selection).
My alteration of the algorithm means that when altering values, you will alter their mirrored values identically (this should happen naturally as a result of the mirrored selection process).
Then go back to step 2 and repeat steps 2-4 until step 3 indicates the algorithm is finished.
This will result in pairs of mirrored answers (which are the coordinates — to get the value of these coordinates refer back to the original matrix) — you can safely delete half of each pair arbitrarily.
To alter this algorithm to find the minimum R pairs, where R is less than the size of the matrix, reduce the stopping point in step 3 to R. This alteration is essential to answering your question.
As #Niklas B, stated you are solving Weighted perfect matching problem
take a look at this
here is part of document describing Primal-dual algorithm for weighted perfect matching
Please read all and let me know if is useful to you

Revisit: 2D Array Sorted Along X and Y Axis

So, this is a common interview question. There's already a topic up, which I have read, but it's dead, and no answer was ever accepted. On top of that, my interests lie in a slightly more constrained form of the question, with a couple practical applications.
Given a two dimensional array such that:
Elements are unique.
Elements are sorted along the x-axis and the y-axis.
Neither sort predominates, so neither sort is a secondary sorting parameter.
As a result, the diagonal is also sorted.
All of the sorts can be thought of as moving in the same direction. That is to say that they are all ascending, or that they are all descending.
Technically, I think as long as you have a >/=/< comparator, any total ordering should work.
Elements are numeric types, with a single-cycle comparator.
Thus, memory operations are the dominating factor in a big-O analysis.
How do you find an element? Only worst case analysis matters.
Solutions I am aware of:
A variety of approaches that are:
O(nlog(n)), where you approach each row separately.
O(nlog(n)) with strong best and average performance.
One that is O(n+m):
Start in a non-extreme corner, which we will assume is the bottom right.
Let the target be J. Cur Pos is M.
If M is greater than J, move left.
If M is less than J, move up.
If you can do neither, you are done, and J is not present.
If M is equal to J, you are done.
Originally found elsewhere, most recently stolen from here.
And I believe I've seen one with a worst-case O(n+m) but a optimal case of nearly O(log(n)).
What I am curious about:
Right now, I have proved to my satisfaction that naive partitioning attack always devolves to nlog(n). Partitioning attacks in general appear to have a optimal worst-case of O(n+m), and most do not terminate early in cases of absence. I was also wondering, as a result, if an interpolation probe might not be better than a binary probe, and thus it occurred to me that one might think of this as a set intersection problem with a weak interaction between sets. My mind cast immediately towards Baeza-Yates intersection, but I haven't had time to draft an adaptation of that approach. However, given my suspicions that optimality of a O(N+M) worst case is provable, I thought I'd just go ahead and ask here, to see if anyone could bash together a counter-argument, or pull together a recurrence relation for interpolation search.
Here's a proof that it has to be at least Omega(min(n,m)). Let n >= m. Then consider the matrix which has all 0s at (i,j) where i+j < m, all 2s where i+j >= m, except for a single (i,j) with i+j = m which has a 1. This is a valid input matrix, and there are m possible placements for the 1. No query into the array (other than the actual location of the 1) can distinguish among those m possible placements. So you'll have to check all m locations in the worst case, and at least m/2 expected locations for any randomized algorithm.
One of your assumptions was that matrix elements have to be unique, and I didn't do that. It is easy to fix, however, because you just pick a big number X=n*m, replace all 0s with unique numbers less than X, all 2s with unique numbers greater than X, and 1 with X.
And because it is also Omega(lg n) (counting argument), it is Omega(m + lg n) where n>=m.
An optimal O(m+n) solution is to start at the top-left corner, that has minimal value. Move diagonally downwards to the right until you hit an element whose value >= value of the given element. If the element's value is equal to that of the given element, return found as true.
Otherwise, from here we can proceed in two ways.
Strategy 1:
Move up in the column and search for the given element until we reach the end. If found, return found as true
Move left in the row and search for the given element until we reach the end. If found, return found as true
return found as false
Strategy 2:
Let i denote the row index and j denote the column index of the diagonal element we have stopped at. (Here, we have i = j, BTW). Let k = 1.
Repeat the below steps until i-k >= 0
Search if a[i-k][j] is equal to the given element. if yes, return found as true.
Search if a[i][j-k] is equal to the given element. if yes, return found as true.
Increment k
1 2 4 5 6
2 3 5 7 8
4 6 8 9 10
5 8 9 10 11

From an interview: Removing rows and columns in an n×n matrix to maximize the sum of remaining values

Given an n×n matrix of real numbers. You are allowed to erase any number (from 0 to n) of rows and any number (from 0 to n) of columns, and after that the sum of the remaining entries is computed. Come up with an algorithm which finds out which rows and columns to erase in order to maximize that sum.
The problem is NP-hard. (So you should not expect a polynomial-time algorithm for solving this problem. There could still be (non-polynomial time) algorithms that are slightly better than brute-force, though.) The idea behind the proof of NP-hardness is that if we could solve this problem, then we could solve the the clique problem in a general graph. (The maximum-clique problem is to find the largest set of pairwise connected vertices in a graph.)
Specifically, given any graph with n vertices, let's form the matrix A with entries a[i][j] as follows:
a[i][j] = 1 for i == j (the diagonal entries)
a[i][j] = 0 if the edge (i,j) is present in the graph (and i≠j)
a[i][j] = -n-1 if the edge (i,j) is not present in the graph.
Now suppose we solve the problem of removing some rows and columns (or equivalently, keeping some rows and columns) so that the sum of the entries in the matrix is maximized. Then the answer gives the maximum clique in the graph:
Claim: In any optimal solution, there is no row i and column j kept for which the edge (i,j) is not present in the graph. Proof: Since a[i][j] = -n-1 and the sum of all the positive entries is at most n, picking (i,j) would lead to a negative sum. (Note that deleting all rows and columns would give a better sum, of 0.)
Claim: In (some) optimal solution, the set of rows and columns kept is the same. This is because starting with any optimal solution, we can simply remove all rows i for which column i has not been kept, and vice-versa. Note that since the only positive entries are the diagonal ones, we do not decrease the sum (and by the previous claim, we do not increase it either).
All of which means that if the graph has a maximum clique of size k, then our matrix problem has a solution with sum k, and vice-versa. Therefore, if we could solve our initial problem in polynomial time, then the clique problem would also be solved in polynomial time. This proves that the initial problem is NP-hard. (Actually, it is easy to see that the decision version of the initial problem — is there a way of removing some rows and columns so that the sum is at least k — is in NP, so the (decision version of the) initial problem is actually NP-complete.)
Well the brute force method goes something like this:
For n rows there are 2n subsets.
For n columns there are 2n subsets.
For an n x n matrix there are 22n subsets.
0 elements is a valid subset but obviously if you have 0 rows or 0 columns the total is 0 so there are really 22n-2+1 subsets but that's no different.
So you can work out each combination by brute force as an O(an) algorithm. Fast. :)
It would be quicker to work out what the maximum possible value is and you do that by adding up all the positive numbers in the grid. If those numbers happen to form a valid sub-matrix (meaning you can create that set by removing rows and/or columns) then there's your answer.
Implicit in this is that if none of the numbers are negative then the complete matrix is, by definition, the answer.
Also, knowing what the highest possible maximum is possibly allows you to shortcut the brute force evaluation since if you get any combination equal to that maximum then that is your answer and you can stop checking.
Also if all the numbers are non-positive, the answer is the maximum value as you can reduce the matrix to a 1 x 1 matrix with that 1 value in it, by definition.
Here's an idea: construct 2n-1 n x m matrices where 1 <= m <= n. Process them one after the other. For each n x m matrix you can calculate:
The highest possible maximum sum (as per above); and
Whether no numbers are positive allowing you to shortcut the answer.
if (1) is below the currently calculate highest maximum sum then you can discard this n x m matrix. If (2) is true then you just need a simple comparison to the current highest maximum sum.
This is generally referred to as a pruning technique.
What's more you can start by saying that the highest number in the n x n matrix is the starting highest maximum sum since obviously it can be a 1 x 1 matrix.
I'm sure you could tweak this into a (slightly more) efficient recursive tree-based search algorithm with the above tests effectively allowing you to eliminate (hopefully many) unnecessary searches.
We can improve on Cletus's generalized brute-force solution by modelling this as a directed graph. The initial matrix is the start node of the graph; its leaves are all the matrices missing one row or column, and so forth. It's a graph rather than a tree, because the node for the matrix without both the first column and row will have two parents - the nodes with just the first column or row missing.
We can optimize our solution by turning the graph into a tree: There's never any point exploring a submatrix with a column or row deleted that comes before the one we deleted to get to the current node, as that submatrix will be arrived at anyway.
This is still a brute-force search, of course - but we've eliminated the duplicate cases where we remove the same rows in different orders.
Here's an example implementation in Python:
def maximize_sum(m):
frontier = [(m, 0, False)]
best = None
best_score = 0
while frontier:
current, startidx, cols_done = frontier.pop()
score = matrix_sum(current)
if score > best_score or not best:
best = current
best_score = score
w, h = matrix_size(current)
if not cols_done:
for x in range(startidx, w):
frontier.append((delete_column(current, x), x, False))
startidx = 0
for y in range(startidx, h):
frontier.append((delete_row(current, y), y, True))
return best_score, best
And here's the output on 280Z28's example matrix:
>>> m = ((1, 1, 3), (1, -89, 101), (1, 102, -99))
>>> maximize_sum(m)
(106, [(1, 3), (1, 101)])
Since nobody asked for an efficient algorithm, use brute force: generate every possible matrix that can be created by removing rows and/or columns from the original matrix, choose the best one. A slightly more efficent version, which most likely can be proved to still be correct, is to generate only those variants where the removed rows and columns contain at least one negative value.
To try it in a simple way:
We need the valid subset of the set of entries {A00, A01, A02, ..., A0n, A10, ...,Ann} which max. sum.
First compute all subsets (the power set).
A valid subset is a member of the power set that for each two contained entries Aij and A(i+x)(j+y), contains also the elements A(i+x)j and Ai(j+y) (which are the remaining corners of the rectangle spanned by Aij and A(i+x)(j+y)).
Aij ...
. .
. .
... A(i+x)(j+y)
By that you can eliminate the invalid ones from the power set and find the one with the biggest sum in the remaining.
I'm sure it can be improved by improving an algorithm for power set generation in order to generate only valid subsets and by that avoiding step 2 (adjusting the power set).
I think there are some angles of attack that might improve upon brute force.
memoization, since there are many distinct sequences of edits that will arrive at the same submatrix.
dynamic programming. Because the search space of matrices is highly redundant, my intuition is that there would be a DP formulation that can save a lot of repeated work
I think there's a heuristic approach, but I can't quite nail it down:
if there's one negative number, you can either take the matrix as it is, remove the column of the negative number, or remove its row; I don't think any other "moves" result in a higher sum. For two negative numbers, your options are: remove neither, remove one, remove the other, or remove both (where the act of removal is either by axing the row or the column).
Now suppose the matrix has only one positive number and the rest are all <=0. You clearly want to remove everything but the positive entry. For a matrix with only 2 positive entries and the rest <= 0, the options are: do nothing, whittle down to one, whittle down to the other, or whittle down to both (resulting in a 1x2, 2x1, or 2x2 matrix).
In general this last option falls apart (imagine a matrix with 50 positives & 50 negatives), but depending on your data (few negatives or few positives) it could provide a shortcut.
Create an n-by-1 vector RowSums, and an n-by-1 vector ColumnSums. Initialize them to the row and column sums of the original matrix. O(n²)
If any row or column has a negative sum, remove edit: the one with the minimum such and update the sums in the other direction to reflect their new values. O(n)
Stop when no row or column has a sum less than zero.
This is an iterative variation improving on another answer. It operates in O(n²) time, but fails for some cases mentioned in other answers, which is the complexity limit for this problem (there are n² entries in the matrix, and to even find the minimum you have to examine each cell once).
Edit: The following matrix has no negative rows or columns, but is also not maximized, and my algorithm doesn't catch it.
1 1 3 goal 1 3
1 -89 101 ===> 1 101
1 102 -99
The following matrix does have negative rows and columns, but my algorithm selects the wrong ones for removal.
-5 1 -5 goal 1
1 1 1 ===> 1
-10 2 -10 2
mine
===> 1 1 1
Compute the sum of each row and column. This can be done in O(m) (where m = n^2)
While there are rows or columns that sum to negative remove the row or column that has the lowest sum that is less than zero. Then recompute the sum of each row/column.
The general idea is that as long as there is a row or a column that sums to nevative, removing it will result in a greater overall value. You need to remove them one at a time and recompute because in removing that one row/column you are affecting the sums of the other rows/columns and they may or may not have negative sums any more.
This will produce an optimally maximum result. Runtime is O(mn) or O(n^3)
I cannot really produce an algorithm on top of my head, but to me it 'smells' like dynamic programming, if it serves as a start point.
Big Edit: I honestly don't think there's a way to assess a matrix and determine it is maximized, unless it is completely positive.
Maybe it needs to branch, and fathom all elimination paths. You never no when a costly elimination will enable a number of better eliminations later. We can short circuit if it's found the theoretical maximum, but other than any algorithm would have to be able to step forward and back. I've adapted my original solution to achieve this behaviour with recursion.
Double Secret Edit: It would also make great strides to reduce to complexity if each iteration didn't need to find all negative elements. Considering that they don't change much between calls, it makes more sense to just pass their positions to the next iteration.
Takes a matrix, the list of current negative elements in the matrix, and the theoretical maximum of the initial matrix. Returns the matrix's maximum sum and the list of moves required to get there. In my mind move list contains a list of moves denoting the row/column removed from the result of the previous operation.
Ie: r1,r1
Would translate
-1 1 0 1 1 1
-4 1 -4 5 7 1
1 2 4 ===>
5 7 1
Return if sum of matrix is the theoretical maximum
Find the positions of all negative elements unless an empty set was passed in.
Compute sum of matrix and store it along side an empty move list.
For negative each element:
Calculate the sum of that element's row and column.
clone the matrix and eliminate which ever collection has the minimum sum (row/column) from that clone, note that action as a move list.
clone the list of negative elements and remove any that are effected by the action taken in the previous step.
Recursively call this algorithm providing the cloned matrix, the updated negative element list and the theoretical maximum. Append the moves list returned to the move list for the action that produced the matrix passed to the recursive call.
If the returned value of the recursive call is greater than the stored sum, replace it and store the returned move list.
Return the stored sum and move list.
I'm not sure if it's better or worse than the brute force method, but it handles all the test cases now. Even those where the maximum contains negative values.
This is an optimization problem and can be solved approximately by an iterative algorithm based on simulated annealing:
Notation: C is number of columns.
For J iterations:
Look at each column and compute the absolute benefit of toggling it (turn it off if it's currently on or turn it on if it's currently off). That gives you C values, e.g. -3, 1, 4. A greedy deterministic solution would just pick the last action (toggle the last column to get a benefit of 4) because it locally improves the objective. But that might lock us into a local optimum. Instead, we probabilistically pick one of the three actions, with probabilities proportional to the benefits. To do this, transform them into a probability distribution by putting them through a Sigmoid function and normalizing. (Or use exp() instead of sigmoid()?) So for -3, 1, 4 you get 0.05, 0.73, 0.98 from the sigmoid and 0.03, 0.42, 0.56 after normalizing. Now pick the action according to the probability distribution, e.g. toggle the last column with probability 0.56, toggle the second column with probability 0.42, or toggle the first column with the tiny probability 0.03.
Do the same procedure for the rows, resulting in toggling one of the rows.
Iterate for J iterations until convergence.
We may also, in early iterations, make each of these probability distributions more uniform, so that we don't get locked into bad decisions early on. So we'd raise the unnormalized probabilities to a power 1/T, where T is high in early iterations and is slowly decreased until it approaches 0. For example, 0.05, 0.73, 0.98 from above, raised to 1/10 results in 0.74, 0.97, 1.0, which after normalization is 0.27, 0.36, 0.37 (so it's much more uniform than the original 0.05, 0.73, 0.98).
It's clearly NP-Complete (as outlined above). Given this, if I had to propose the best algorithm I could for the problem:
Try some iterations of quadratic integer programming, formulating the problem as: SUM_ij a_ij x_i y_j, with the x_i and y_j variables constrained to be either 0 or 1. For some matrices I think this will find a solution quickly, for the hardest cases it would be no better than brute force (and not much would be).
In parallel (and using most of the CPU), use a approximate search algorithm to generate increasingly better solutions. Simulating Annealing was suggested in another answer, but having done research on similar combinatorial optimisation problems, my experience is that tabu search would find good solutions faster. This is probably close to optimal in terms of wandering between distinct "potentially better" solutions in the shortest time, if you use the trick of incrementally updating the costs of single changes (see my paper "Graph domination, tabu search and the football pool problem").
Use the best solution so far from the second above to steer the first by avoiding searching possibilities that have lower bounds worse than it.
Obviously this isn't guaranteed to find the maximal solution. But, it generally would when this is feasible, and it would provide a very good locally maximal solution otherwise. If someone had a practical situation requiring such optimisation, this is the solution that I'd think would work best.
Stopping at identifying that a problem is likely to be NP-Complete will not look good in a job interview! (Unless the job is in complexity theory, but even then I wouldn't.) You need to suggest good approaches - that is the point of a question like this. To see what you can come up with under pressure, because the real world often requires tackling such things.
yes, it's NP-complete problem.
It's hard to easily find the best sub-matrix,but we can easily to find some better sub-matrix.
Assume that we give m random points in the matrix as "feeds". then let them to automatically extend by the rules like :
if add one new row or column to the feed-matrix, ensure that the sum will be incrementive.
,then we can compare m sub-matrix to find the best one.
Let's say n = 10.
Brute force (all possible sets of rows x all possible sets of columns) takes
2^10 * 2^10 =~ 1,000,000 nodes.
My first approach was to consider this a tree search, and use
the sum of positive entries is an upper bound for every node in the subtree
as a pruning method. Combined with a greedy algorithm to cheaply generate good initial bounds, this yielded answers in about 80,000 nodes on average.
but there is a better way ! i later realised that
Fix some choice of rows X.
Working out the optimal columns for this set of rows is now trivial (keep a column if its sum of its entries in the rows X is positive, otherwise discard it).
So we can just brute force over all possible choices of rows; this takes 2^10 = 1024 nodes.
Adding the pruning method brought this down to 600 nodes on average.
Keeping 'column-sums' and incrementally updating them when traversing the tree of row-sets should allow the calculations (sum of matrix etc) at each node to be O(n) instead of O(n^2). Giving a total complexity of O(n * 2^n)
For slightly less than optimal solution, I think this is a PTIME, PSPACE complexity issue.
The GREEDY algorithm could run as follows:
Load the matrix into memory and compute row totals. After that run the main loop,
1) Delete the smallest row,
2) Subtract the newly omitted values from the old row totals
--> Break when there are no more negative rows.
Point two is a subtle detail: subtracted two rows/columns has time complexity n.
While re-summing all but two columns has n^2 time complexity!
Take each row and each column and compute the sum. For a 2x2 matrix this will be:
2 1
3 -10
Row(0) = 3
Row(1) = -7
Col(0) = 5
Col(1) = -9
Compose a new matrix
Cost to take row Cost to take column
3 5
-7 -9
Take out whatever you need to, then start again.
You just look for negative values on the new matrix. Those are values that actually substract from the overall matrix value. It terminates when there're no more negative "SUMS" values to take out (therefore all columns and rows SUM something to the final result)
In an nxn matrix that would be O(n^2)Log(n) I think
function pruneMatrix(matrix) {
max = -inf;
bestRowBitField = null;
bestColBitField = null;
for(rowBitField=0; rowBitField<2^matrix.height; rowBitField++) {
for (colBitField=0; colBitField<2^matrix.width; colBitField++) {
sum = calcSum(matrix, rowBitField, colBitField);
if (sum > max) {
max = sum;
bestRowBitField = rowBitField;
bestColBitField = colBitField;
}
}
}
return removeFieldsFromMatrix(bestRowBitField, bestColBitField);
}
function calcSumForCombination(matrix, rowBitField, colBitField) {
sum = 0;
for(i=0; i<matrix.height; i++) {
for(j=0; j<matrix.width; j++) {
if (rowBitField & 1<<i && colBitField & 1<<j) {
sum += matrix[i][j];
}
}
}
return sum;
}

Resources