Expanding on TheGreatContini's idea:
First try
Let's look at it as finding combinations that belong to AxB, with A and B sets of rows. These combinations must satisfy the or condition, but we'll also assume the hamming weight of a is at least as large as b (to avoid some duplicates).
Now split A into A0 (rows that start with 0) and A1 (rows that start with 1). Do the same for B. We have now reduced the problem into three smaller problems: A0xB1, A1xB1 and A1xB0. If A and B were the same, A0xB1 and A1xB0 are the same, so we only need to do one. Not only are these three subproblems combined smaller than the first, we've also fully checked the first column and can ignore it from now on.
To solve these subproblems, we can use the same approach, but now with column 2, 3, ... At some point, either we will have checked all the columns or #A and #B will be 1.
Depending on the implementation, stopping sooner might be more efficient. At that point we can do an exhaustive check of the remaining combinations. Remember though that if we have already checked k columns, that this will only cost m-k per combination.
Better column selection
As TheGreatContini suggested, instead of selecting the first column, we can select the column that would lead to the smallest subproblems. The cost of finding this column at each step is rather high, but the weights could be calculated once in the beginning and then used as an estimate for the best column. We can then rearrange the columns use the algorithm as normal, after it is done, rearrange them again.
The exact best column would be the column for which the amount of zeros in A times the amount of zeros in B is maximal.
Hamming weight pruning
We know that the sum of the hamming weights of a and b must be at least m. And since we assumed a to be the highest hamming weight, we can remove all the values of a that have a hamming weight less than m/2. (the speedup this gives might be negligable, i'm not sure). Calculating all the hamming weights costs O(m*n).
Efficient Splitting
If we sort the rows, the splitting in groups can be done much faster using the bisection algorithm. This can also lead to an efficient representation of the sets in memory. We can just specify the minimum and maximum row. Sorting can be done in O(n*m*log(n)). Splitting can then be done in about log(n).
Here's some code that won't compile, but should give the right idea.
private List<Row> rows;
public int findFirstOne(int column, int start, int end){
if(rows.get(start).get(column) == 1) return start;
if(rows.get(end).get(column) == 0) return -1;
while(start < end){
int mid = (start+end)/2;
if(rows.get(mid).get(column) == 0){
start = mid+1;
}else{
end = mid;
}
}
return start;
}
Complexity
In the following calculations the effect of the better column selection is ignored as it will give little improvement on worst case efficienty. However, in average cases it can give a massive improvement by reducing the search space as soon as possible and thereby making the other checks faster.
The algorithms run time is bounded by n²m.
However, the worst examples i have found are all O(n*log(n)*m).
First, the sorting of the matrix will be O(n*log(n)*m) for the rows and optionally, sorting the columns will be O(n*m + m*log(m)).
Then, the creating of the subproblems. Let's make an overestimate first. We need to subdivise at most m times and the cost for a full level of of subdivisions at depth i can be overestimated as log(n)*3^i (cost per subdivision times number of subdivisions). This leads to O(log(n)*3^m) in total.
It must also hold that 3^i <= n²/2, because this is the maximum number of combinations possible, so for large m it caps at O(n²*log(n)*m). I struggle to find an example that actually behaves like this though.
I think it is reasonable to assume many of the subproblems become trivial very early. Leading to O(log(n)*m*n) (if someone wants to check this, i'm not really sure about it).
O(n * n)
time. – Nolam
is3000
. You can't store it in an integer. – PedicularO(n*n*m)
solution passed. I was just curious for a better solution :).....or searching for a proof that no further optimization is possible. And yes the number of test cases were<= 10
. Time of running<= 1sec
. – Pedicularn^2*m
complexity. Was expecting a mathematical proof for that but I think some intuition and analysis will suffice. That's why I have marked your answer as correct :) . It presented sufficient analysis for the maximum optimization possible. Thank you ! – Pedicular