Order a set of numbers to maximize adjacent differences - sorting

Given a set of N numbers x1, x2, ..., xN, how can you find an ordering of them to maximize the minimum absolute difference between adjacent numbers? This is probably an NP hard problem, so any efficient approximate method will do.

Let's say you've defined your data as x_i for i=1, ..., n. We can define binary variables p_{ij} for i=1, ..., n, and j=1, ..., n, which are 1 if number i is in sorted order j and 0 otherwise. Adding a variable e, our optimization model would be something like:
The constraints with the absolute values ensure that e (our minimum gap) does not exceed the gap between each pair of adjacent elements in our sorted sequence. However, absolute values aren't allowed in linear optimization models, and in general you need to add a binary variable to model an absolute value being greater than or equal to some other value. So let's add binary variable r_j, j=2, ..., n, and replace our problematic constraints:
Here M is a large number; 2(max(x) - min(x)) should be sufficiently large. Now, we're ready to actually implement this model. You can use any MIP solver; I'll use the lpSolveAPI in R because it's free and easily accessible. p_{ij} are stored in variables 1 through n^2; r_j are stored in variables n^2+1 through n^2+n-1; and e is stored in variable n^2+n.
x = 1:5
n = length(x)
M = 2*(max(x) - min(x))
library(lpSolveAPI)
mod = make.lp(0, n^2+n)
set.type(mod, 1:(n^2+n-1), "binary")
set.objfn(mod, c(rep(0, n^2+n-1), 1))
lp.control(mod, sense="max")
for (j in 2:n) {
base.cons <- rep(0, n^2+n)
base.cons[seq(j-1, by=n, length.out=n)] = x
base.cons[seq(j, by=n, length.out=n)] = -x
base.cons[n^2+j-1] = M
first.cons = base.cons
first.cons[n^2+n] = -1
add.constraint(mod, first.cons, ">=", 0)
second.cons = -base.cons
second.cons[n^2+n] = -1
add.constraint(mod, second.cons, ">=", -M)
}
for (j in 1:n) {
this.cons = rep(0, n^2+n)
this.cons[seq(j, by=n, length.out=n)] = 1
add.constraint(mod, this.cons, "=", 1)
}
for (i in 1:n) {
this.cons = rep(0, n^2+n)
this.cons[seq((i-1)*n+1, i*n)] = 1
add.constraint(mod, this.cons, "=", 1)
}
Now we're ready to solve the model:
solve(mod)
# [1] 0
get.objective(mod)
# [1] 2
get.variables(mod)
# [1] 1 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 1 0 0 1 0 0 1 1 0 1 2
And lastly we can extract the sorted list using the x_i and p_{ij} variables:
sapply(1:n, function(j) sum(get.variables(mod)[seq(j, by=n, length.out=n)]*x))
# [1] 1 3 5 2 4

Related

How to sort different related units?

I've given a task in which the user enters some unit relations and we have to sort them from high to low.
What is the best algorithm to do that?
I put some input/output pairs to clarify the problem:
Input:
km = 1000 m
m = 100 cm
cm = 10 mm
Output:
1km = 1000m = 100000cm = 1000000mm
Input:
km = 100000 cm
km = 1000000 mm
m = 1000 mm
Output:
1km = 1000m = 100000cm = 1000000mm
Input:
B = 8 b
MiB = 1024 KiB
KiB = 1024 B
Mib = 1048576 b
Mib = 1024 Kib
Output:
1MiB = 8Mib = 1024KiB = 8192Kib = 1048576B = 8388608b
Input:
B = 8 b
MiB = 1048576 B
MiB = 1024 KiB
MiB = 8192 Kib
MiB = 8 Mib
Output:
1MiB = 8Mib = 1024KiB = 8192Kib = 1048576B = 8388608b
How to generate output based on given output?
My attempt at a graph-based solution. Example 3 is the most interesting, so I'll take that one, (multiple steps and multiple sinks.)
Transform B = n A to edge A -> B and label it n, n > 1. If it's not a connected DAG, it's inconsistent.
Reduce to a bipartite graph by making multiple connections I -> J -> K skip to I -> K by multiplying the n of I -> J by J -> K. Any inconsistencies are a sign that the problem is inconsistent.
The idea of this step is to produce only one single greatest value. A vertex on the left with a degree of greater than 1, P, and { Q, R } are in the right set, where, P -> Q labelled n1 and P -> R labelled n2, 1 < n1 < n2, (WLOG,) can be transformed into P -> R (unchanged) and Q -> R with label n2 / n1 (bringing Q, in this case Mib, from right to left.)
Is the graph bipartite with a single right node? No, goto 2.
Sort the edges.
X -> Z with n1 ... Y -> Z with n2 becomes 1 Z = n1 X = ... = n2 Y.
You can find the following algorithm:
1. detect all existing units: `n` units
2. create a `n x n` matrix `M` such that the same rows and columns show
the corresponding unit. put all elements of the main diagonal of the
matrix to `1`.
3. put the specified value in the input into the corresponding row and column.
4. put zero for the transpose of the row and the column in step 3.
5. put `-1` for all other elements
Now, based on `M` you can easily find the biggest unit:
5.1 candidate_maxs <-- Find columns with only one non-zero positive element
not_max <-- []
6. while len(candidate_max)> 1:
a. take a pair <i, l> and find a column h such that both (i, h)
and (l, h) are known, i.e., they are positive.
If M[i, h] > M[l, h]:
remove_item <-- l
Else:
remove_item <-- i
candidate_max.remove(remove_item)
not_max.append(remove_item)
b. if cannot find such a pair, find a pair <i, l>: i from
candidate_max and h from not_max with the same property.
If M[i, h] < M[l, h]:
candidate_max.remove(i)
not_max.append(i)
biggest_unit <-- The only element of candidate_max
By finding the biggest unit, you can order others based on their value in the corresponding row of the biggest_unit.
7. while there is `-1` value in the row `biggest_unit` on column `j`:
`(biggest_unit, j)`
a. find a non-identity and non-zero positive element in (column `j`
and row `k`) or (row `j` and column `k`), i.e., `(k,j)` or `(j, k)`, such that `(biggest_unit, k)` is strictly
positive and non-identity. Then, calculate the missing value
based on the found equivalences.
b. if there is not such a row, continue the loop with another `-1`
unit element.
8. sort units based on their column value in `biggest_unit` row in
ascending order.
However, the time complexity of the algorithm is Theta(n^2) that n is the number of units (if you implement the loop on step 6 wisely!).
Example
Input 1
km = 1000 m
m = 100 cm
cm = 10 mm
Solution:
km m cm mm
km 1 1000 -1 -1
m 0 1 100 -1
cm -1 0 1 10
mm -1 -1 0 1
M = [1 1000 -1 -1
0 1 100 -1
-1 0 1 10
-1 -1 0 1]
===> 6. `biggest_unit` <--- km (column 1)
7.1 Find first `-1` in the first row and column 3: (1,3)
Find strictly positive value in row 2 such that (1,2) is strictly
positive and non-identity. So, the missing value of `(1,3)` must be
`1000 * 100 = 100000`.
7.2 Find the second `-1` in the first row and column 4: (1,4)
Find strictly positive value in row 3 such that (1,3) is strictly
positive and non-identity. So, the missing value of `(1,4)` must be
`100000 * 10 = 1000000`.
The loop is finished here and we have:
M = [1 1000 100000 1000000
0 1 100 -1
-1 0 1 10
-1 -1 0 1]
Now you can sort the elements of the first row in ascending order.
Input 2
km = 100000 cm
km = 1000000 mm
m = 1000 mm
Solution:
km m cm mm
km 1 -1 100000 1000000
m -1 1 -1 1000
cm 0 -1 1 -1
mm 0 0 -1 1
M = [1 -1 100000 1000000
-1 1 -1 1000
0 -1 1 -1
0 0 -1 1]
===>
6.1 candidate_max = [1, 2]
6.2 Compare them on column 4 and remove 2
biggest_unit <-- column 1
And by going forward on step 7,
Find first `-1` in the first row and column 2: (1,2)
Find a strictly positive and non-identity value in row 2:(1,4)
So, the missing value of `(1,2)` must be `1000000 / 1000 = 1000`.
In sum, we have:
M = [1 1000 100000 1000000
-1 1 -1 1000
0 -1 1 -1
0 0 -1 1]
Now you can sort the elements of the first row in ascending order (step 8).

Algorithm for generating strings of +/-s with a specific property

I am interested in writing a function generate(n,m) which exhaustively generating strings of length n(n-1)/2 consisting solely of +/- characters. These strings will then be transformed into an n × n symmetric (-1,0,1)-matrix in the following way:
toTriangle["+--+-+-++-"]
{{1, -1, -1, 1}, {-1, 1, -1}, {1, 1}, {-1}}
toMatrix[%, 0] // MatrixForm
| 0 1 -1 -1 1 |
| 1 0 -1 1 -1 |
matrixForm = |-1 -1 0 1 1 |
|-1 1 1 0 -1 |
| 1 -1 1 -1 0 |
Thus the given string represents the upper-right triangle of the matrix, which is then reflected to generate the rest of it.
Question: How can I generate all +/- strings such that the resulting matrix has precisely m -1's per row?
For example, generate(5,3) will give all strings of length 5(5-1)/2 = 10 such that each row contains precisely three -1's.
I'd appreciate any help with constructing such an algorithm.
This is the logic to generate every matrix for a given n and m. It's a bit convoluted, so I'm not sure how much faster than brute force an implementation would be; I assume the difference will become more pronounced for larger values.
(The following will generate an output of zeros and ones for convenience, where zero represents a plus and a one represents a minus.)
A square matrix where each row has m ones translates to a triangular matrix where these folded row/columns have m ones:
x 0 1 0 1 x 0 1 0 1 0 1 0 1
0 x 1 1 0 x 1 1 0 1 1 0
1 1 x 0 0 x 0 0 0 0
0 1 0 x 1 x 1 1
1 0 0 1 x x
Each of these groups overlaps with all the other groups; choosing values for the first k groups means that the vertical part of group k+1 is already determined.
We start by putting the number of ones required per row on the diagonal; e.g. for (5,2) that is:
2 . . . .
2 . . .
2 . .
2 .
2
Then we generate every bit pattern with m ones for the first group; there are (n-1 choose m) of these, and they can be efficiently generated, e.g. with Gosper's hack.
(4,2) -> 0011 0101 0110 1001 1010 1100
For each of these, we fill them in in the matrix, and subtract them from the numbers of required ones:
X 0 0 1 1
2 . . .
2 . .
1 .
1
and then recurse with the smaller triangle:
2 . . .
2 . .
1 .
1
If we come to a point where some of the numbers of required ones on the diagonal are zero, e.g.:
2 . . .
1 . .
0 .
1
then we can already put a zero in this column, and generate the possible bit patterns for fewer columns; in the example that would be (2,2) instead of (3,2), so there's only one possible bit pattern: 11. Then we distribute the bit pattern over the columns that have a non-zero required count under them:
2 . 0 . X 1 0 1
1 . . 0 . .
0 . 0 .
1 0
However, not all possible bit patterns will lead to valid solutions; take this example:
2 . . . . X 0 0 1 1
2 . . . 2 . . . 2 . . . X 0 1 1
2 . . 2 . . 2 . . 2 . . 2 . .
2 . 1 . 1 . 0 . 0 .
2 1 1 0 0
where we end up with a row that requires another 2 ones while both columns can no longer take any ones. The way to spot this situation is by looking at the list of required ones per column that is created by each option in the penultimate step:
pattern required
0 1 1 -> 2 0 0
1 0 1 -> 1 1 0
1 1 0 -> 1 0 1
If the first value in the list is x, then there must be at least x non-zero values after it; which is false for the first of the three options.
(There is room for optimization here: in a count list like 1,1,0,6,0,2,1,1 there are only 2 non-zero values before the 6, which means that the 6 will be decremented at most 2 times, so its minimum value when it becomes the first element will be 4; however, there are only 3 non-zero values after it, so at this stage you already know this list will not lead to any valid solutions. Checking this would add to the code complexity, so I'm not sure whether that would lead to an improvement in execution speed.)
So the complete algorithm for (n,m) starts with:
Create an n-sized list with all values set to m (count of ones required per group).
Generate all bit patterns of size n-1 with m ones; for each of these:
Subtract the pattern from a copy of the count list (without the first element).
Recurse with the pattern and the copy of the count list.
and the recursive steps after that are:
Receive the sequence so far, and a count list.
The length of the count list is n, and its first element is m.
Let k be the number of non-zero values in the count list (without the first element).
Generate all bit pattern of size k with m ones; for each of these:
Create a 0-filled list sized n-1.
Distribute the bit pattern over it, skipping the columns with a zero count.
Add the value list to the sequence so far.
Subtract the value list from a copy of the count list (without the first element).
If the first value in the copy of the count list is greater than the number of non-zeros after it, skip this pattern.
At the deepest recursion level, store the sequence, or else:
Recurse with the sequence so far, and the copy of the count list.
Here's a code snippet as a proof of concept; in a serious language, and using integers instead of arrays for the bitmaps, this should be much faster:
function generate(n, m) {
// if ((n % 2) && (m % 2)) return; // to catch (3,1)
var counts = [], pattern = [];
for (var i = 0; i < n - 1; i++) {
counts.push(m);
pattern.push(i < m ? 1 : 0);
}
do {
var c_copy = counts.slice();
for (var i = 0; i < n - 1; i++) c_copy[i] -= pattern[i];
recurse(pattern, c_copy);
}
while (revLexi(pattern));
}
function recurse(sequence, counts) {
var n = counts.length, m = counts.shift(), k = 0;
for (var i = 0; i < n - 1; i++) if (counts[i]) ++k;
var pattern = [];
for (var i = 0; i < k; i++) pattern.push(i < m ? 1 : 0);
do {
var values = [], pos = 0;
for (var i = 0; i < n - 1; i++) {
if (counts[i]) values.push(pattern[pos++]);
else values.push(0);
}
var s_copy = sequence.concat(values);
var c_copy = counts.slice();
var nonzero = 0;
for (var i = 0; i < n - 1; i++) {
c_copy[i] -= values[i];
if (i && c_copy[i]) ++nonzero;
}
if (c_copy[0] > nonzero) continue;
if (n == 2) {
for (var i = 0; i < s_copy.length; i++) {
document.write(["+ ", "− "][s_copy[i]]);
}
document.write("<br>");
}
else recurse(s_copy, c_copy);
}
while (revLexi(pattern));
}
function revLexi(seq) { // reverse lexicographical because I had this lying around
var max = true, pos = seq.length, set = 1;
while (pos-- && (max || !seq[pos])) if (seq[pos]) ++set; else max = false;
if (pos < 0) return false;
seq[pos] = 0;
while (++pos < seq.length) seq[pos] = set-- > 0 ? 1 : 0;
return true;
}
generate(5, 2);
Here are the number of results and the number of recursions for values of n up to 10, so you can compare them to check correctness. When n and m are both odd numbers, there are no valid results; this is calculated correctly, except in the case of (3,1); it is of course easy to catch these cases and return immediately.
(n,m) results number of recursions
(4,0) (4,3) 1 2 2
(4,1) (4,2) 3 6 7
(5,0) (5,4) 1 3 3
(5,1) (5,3) 0 12 20
(5,2) 12 36
(6,0) (6,5) 1 4 4
(6,1) (6,4) 15 48 76
(6,2) (6,3) 70 226 269
(7,0) (7,6) 1 5 5
(7,1) (7,5) 0 99 257
(7,2) (7,4) 465 1,627 2,313
(7,3) 0 3,413
(8,0) (8,7) 1 6 6
(8,1) (8,6) 105 422 1,041
(8,2) (8,5) 3,507 13,180 23,302
(8,3) (8,4) 19,355 77,466 93,441
(9,0) (9,8) 1 7 7
(9,1) (9,7) 0 948 4,192
(9,2) (9,6) 30,016 119,896 270,707
(9,3) (9,5) 0 1,427,457 2,405,396
(9,4) 1,024,380 4,851,650
(10,0) (10,9) 1 8 8
(10,1) (10,8) 945 4440 18930
(10,2) (10,7) 286,884 1,210,612 3,574,257
(10,3) (10,6) 11,180,820 47,559,340 88,725,087
(10,4) (10,5) 66,462,606 313,129,003 383,079,169
I doubt that you really want all variants for large n,m values - number of them is tremendous large.
This problem is equivalent to generation of m-regular graphs (note that if we replace all 1's by zeros and all -1's by 1 - we can see adjacency matrix of graph. Regular graph - degrees of all vertices are equal to m).
Here we can see that number of (18,4) regular graphs is about 10^9 and rises fast with n/m values. Article contains link to program genreg intended for such graphs generation. FTP links to code and executable don't work for me - perhaps too old.
Upd: Here is another link to source (though 1996 year instead of paper's 1999)
Simple approach to generate one instance of regular graph is described here.
For small n/m values you can also try brute-force: fill the first row with m ones (there are C(n,m) variants and for every variants fill free places in the second row and so on)
Written in Wolfram Mathematica.
generate[n_, m_] := Module[{},
x = Table[StringJoin["i", ToString[i], "j", ToString[j]],
{j, 1, n}, {i, 2, n}];
y = Transpose[x];
MapThread[(x[[#, ;; #2]] = y[[#, ;; #2]]) &,
{-Range[n - 1], Reverse#Range[n - 1]}];
Clear ## Names["i*"];
z = ToExpression[x];
Clear[s];
s = Reduce[Join[Total## == m & /# z,
0 <= # <= 1 & /# Union[Flatten#z]],
Union#Flatten[z], Integers];
Clear[t, u, v];
Array[(t[#] =
Partition[Flatten[z] /.
ToRules[s[[#]]], n - 1] /.
{1 -> -1, 0 -> 1}) &, Length[s]];
Array[Function[a,
(u[a] = StringJoin[Flatten[MapThread[
Take[#, 1 - #2] &,
{t[a], Reverse[Range[n]]}]] /.
{1 -> "+", -1 -> "-"}])], Length[s]];
Array[Function[a,
(v[a] = MapThread[Insert[#, 0, #2] &,
{t[a], Range[n]}])], Length[s]]]
Timing[generate[9, 4];]
Length[s]
{202.208, Null}
1024380
The program takes 202 seconds to generate 1,024,380 solutions. E.g. the last one
u[1024380]
----++++---++++-+-+++++-++++--------
v[1024380]
0 -1 -1 -1 -1 1 1 1 1
-1 0 -1 -1 -1 1 1 1 1
-1 -1 0 -1 1 -1 1 1 1
-1 -1 -1 0 1 1 -1 1 1
-1 -1 1 1 0 1 1 -1 -1
1 1 -1 1 1 0 -1 -1 -1
1 1 1 -1 1 -1 0 -1 -1
1 1 1 1 -1 -1 -1 0 -1
1 1 1 1 -1 -1 -1 -1 0
and the first ten strings
u /# Range[10]
++++----+++----+-+-----+----++++++++
++++----+++----+-+------+--+-+++++++
++++----+++----+-+-------+-++-++++++
++++----+++----+--+---+-----++++++++
++++----+++----+---+--+----+-+++++++
++++----+++----+----+-+----++-++++++
++++----+++----+--+-----+-+--+++++++
++++----+++----+--+------++-+-++++++
++++----+++----+---+---+--+--+++++++

How to find all possible reachable numbers from a position?

Given 2 elements n, s and an array A of size m, where s is initial position which lies between 1 <= s <= n, our task is to perform m operations to s and in each operation we either make s = s + A[i] or s = s - A[i], and we have to print all the values which are possible after the m operation and all those value should lie between 1 - n (inclusive).
Important Note: If during an operation we get a value s < 1 or s > n,
we don't go further with that value of s.
I solved the problem using BFS, but the problem is BFS approach is not optimal here, can someone suggest any other more optimal approach to me or an algorithm will greatly help.
For example:-
If n = 3, s = 3, and A = {1, 1, 1}
3
/ \
operation 1: 2 4 (we don’t proceed with 4 as it is > n)
/ \ / \
operation 2: 1 3 3 5
/ \ / \ / \ / \
operation 3: 0 2 2 4 2 4 4 6
So final values reachable by following above rules are 2 and 2 (that is two times 2). we don't consider the third two as it has an intermediate state which is > n ( same case applicable if < 1).
There is this dynamic programming solution, which runs in O(nm) time and requires O(n) space.
First establish a boolean array called reachable, initialize it to false everywhere except for reachable[s], which is true.
This array now represents whether a number is reachable in 0 steps. Now for every i from 1 to m, we update the array so that reachable[x] represents whether the number x is reachable in i steps. This is easy: x is reachable in i steps if and only if either x - A[i] or x + A[i] is reachable in i - 1 steps.
In the end, the array becomes the final result you want.
EDIT: pseudo-code here.
// initialization:
for x = 1 to n:
r[x] = false
r[s] = true
// main loop:
for k = 1 to m:
for x = 1 to n:
last_r[x] = r[x]
for x = 1 to n:
r[x] = (last_r[x + A[k]] or last_r[x - A[k]])
Here last_r[x] is by convention false if x is not in the range [1 .. n].
If you want to maintain the number of ways that each number can be reached, then you do the following changes:
Change the array r to an integer array;
In the initialization, initialize all r[x] to 0, except r[s] to 1;
In the main loop, change the key line to:
r[x] = last_r[x + A[k]] + last_r[x - A[k]]

All possible N choose K WITHOUT recusion

I'm trying to create a function that is able to go through a row vector and output the possible combinations of an n choose k without recursion.
For example: 3 choose 2 on [a,b,c] outputs [a,b; a,c; b,c]
I found this: How to loop through all the combinations of e.g. 48 choose 5 which shows how to do it for a fixed n choose k and this: https://codereview.stackexchange.com/questions/7001/generating-all-combinations-of-an-array which shows how to get all possible combinations. Using the latter code, I managed to make a very simple and inefficient function in matlab which returned the result:
function [ combi ] = NCK(x,k)
%x - row vector of inputs
%k - number of elements in the combinations
combi = [];
letLen = 2^length(x);
for i = 0:letLen-1
temp=[0];
a=1;
for j=0:length(x)-1
if (bitand(i,2^j))
temp(k) = x(j+1);
a=a+1;
end
end
if (nnz(temp) == k)
combi=[combi; derp];
end
end
combi = sortrows(combi);
end
This works well for very small vectors, but I need this to be able to work with vectors of at least 50 in length. I've found many examples of how to do this recursively, but is there an efficient way to do this without recursion and still be able to do variable sized vectors and ks?
Here's a simple function that will take a permutation of k ones and n-k zeros and return the next combination of nchoosek. It's completely independent of the values of n and k, taking the values directly from the input array.
function [nextc] = nextComb(oldc)
nextc = [];
o = find(oldc, 1); %// find the first one
z = find(~oldc(o+1:end), 1) + o; %// find the first zero *after* the first one
if length(z) > 0
nextc = oldc;
nextc(1:z-1) = 0;
nextc(z) = 1; %// make the first zero a one
nextc(1:nnz(oldc(1:z-2))) = 1; %// move previous ones to the beginning
else
nextc = zeros(size(oldc));
nextc(1:nnz(oldc)) = 1; %// start over
end
end
(Note that the else clause is only necessary if you want the combinations to wrap around from the last combination to the first.)
If you call this function with, for example:
A = [1 1 1 1 1 0 1 0 0 1 1]
nextCombination = nextComb(A)
the output will be:
A =
1 1 1 1 1 0 1 0 0 1 1
nextCombination =
1 1 1 1 0 1 1 0 0 1 1
You can then use this as a mask into your alphabet (or whatever elements you want combinations of).
C = ['a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k']
C(find(nextCombination))
ans = abcdegjk
The first combination in this ordering is
1 1 1 1 1 1 1 1 0 0 0
and the last is
0 0 0 1 1 1 1 1 1 1 1
To generate the first combination programatically,
n = 11; k = 8;
nextCombination = zeros(1,n);
nextCombination(1:k) = 1;
Now you can iterate through the combinations (or however many you're willing to wait for):
for c = 2:nchoosek(n,k) %// start from 2; we already have 1
nextCombination = nextComb(A);
%// do something with the combination...
end
For your example above:
nextCombination = [1 1 0];
C(find(nextCombination))
for c = 2:nchoosek(3,2)
nextCombination = nextComb(nextCombination);
C(find(nextCombination))
end
ans = ab
ans = ac
ans = bc
Note: I've updated the code; I had forgotten to include the line to move all of the 1's that occur prior to the swapped digits to the beginning of the array. The current code (in addition to being corrected above) is on ideone here. Output for 4 choose 2 is:
allCombs =
1 2
1 3
2 3
1 4
2 4
3 4

Finding maximum size sub-matrix of all 1's in a matrix having 1's and 0's

Suppose you are given an mXn bitmap, represented by an array M[1..m,1.. n] whose entries are all 0 or 1. A all-one block is a subarray of the form M[i .. i0, j .. j0] in which every bit is equal to 1. Describe and analyze an efficient algorithm to find an all-one block in M with maximum area
I am trying to make a dynamic programming solution. But my recursive algorithm runs in O(n^n) time, and even after memoization I cannot think of bringing it down below O(n^4). Can someone help me find a more efficient solution?
An O(N) (number of elements) solution:
A
1 1 0 0 1 0
0 1 1 1 1 1
1 1 1 1 1 0
0 0 1 1 0 0
Generate an array C where each element represents the number of 1s above and including it, up until the first 0.
C
1 1 0 0 1 0
0 2 1 1 2 1
1 3 2 2 3 0
0 0 3 3 0 0
We want to find the row R, and left, right indices l , r that maximizes (r-l+1)*min(C[R][l..r]). Here is an algorithm to inspect each row in O(cols) time:
Maintain a stack of pairs (h, i), where C[R][i-1] < h ≤ C[R][i]. At any position cur, we should have h=min(C[R][i..cur]) for all pairs (h, i) on the stack.
For each element:
If h_cur>h_top
Push (h, i).
Else:
While h_cur<h_top:
Pop the top of the stack.
Check whether it would make a new best, i.e. (i_cur-i_pop)*h_pop > best.
If h_cur>h_top
Push (h, i_lastpopped).
An example of this in execution for the third row in our example:
i =0 1 2 3 4 5
C[i]=1 3 2 2 3 0
(3, 4)
S= (3, 1) (2, 1) (2, 1) (2, 1)
(1, 0) (1, 0) (1, 0) (1, 0) (1, 0)
(0,-1) (0,-1) (0,-1) (0,-1) (0,-1) (0,-1)
i=0, C[i]=1) Push (1, 0).
i=1, C[i]=3) Push (3, 1).
i=2, C[i]=2) Pop (3, 1). Check whether (2-1)*3=3 is a new best.
        The last i popped was 1, so push (2, 1).
i=3, C[i]=2) h_cur=h_top so do nothing.
i=4, C[i]=3) Push (3, 4).
i=5, C[i]=0) Pop (3, 4). Check whether (5-4)*3=3 is a new best.
        Pop (2, 1). Check whether (5-1)*2=8 is a new best.
        Pop (1, 0). Check whether (5-0)*1=5 is a new best.
        End. (Okay, we should probably add an extra term C[cols]=0 on the end for good measure).
Here's an O(numCols*numLines^2) algorithm. Let S[i][j] = sum of the first i elements of column j.
I will work the algorithm on this example:
M
1 1 0 0 1 0
0 1 1 1 0 1
1 1 1 1 0 0
0 0 1 1 0 0
We have:
S
1 1 0 0 1 0
1 2 1 1 1 1
2 3 2 2 1 1
2 3 3 3 1 1
Now consider the problem of finding the maximum subarray of all ones in a one-dimensional array. This can be solved using this simple algorithm:
append 0 to the end of your array
max = 0, temp = 0
for i = 1 to array.size do
if array[i] = 1 then
++temp
else
if temp > max then
max = temp
temp = 0
For example, if you have this 1d array:
1 2 3 4 5 6
1 1 0 1 1 1
you'd do this:
First append a 0:
1 2 3 4 5 6 7
1 1 0 1 1 1 0
Now, notice that whenever you hit a 0, you know where a sequence of contiguous ones ends. Therefore, if you keep a running total (temp variable) of the current number of ones, you can compare that total with the maximum so far (max variable) when you hit a zero, and then reset the running total. This will give you the maximum length of a contiguous sequence of ones in the variable max.
Now you can use this subalgorithm to find the solution for your problem. First of all append a 0 column to your matrix. Then compute S.
Then:
max = 0
for i = 1 to M.numLines do
for j = i to M.numLines do
temp = 0
for k = 1 to M.numCols do
if S[j][k] - S[i-1][k] = j - i + 1 then
temp += j - i + 1
else
if temp > max then
max = temp
temp = 0
Basically, for each possible height of a subarray (there are O(numLines^2) possible heights), you find the one with maximum area having that height by applying the algorithm for the one-dimensional array (in O(numCols)).
Consider the following "picture":
M
1 1 0 0 1 0 0
i 0 1 1 1 0 1 0
j 1 1 1 1 0 0 0
0 0 1 1 0 0 0
This means that we have the height j - i + 1 fixed. Now, take all the elements of the matrix that are between i and j inclusively:
0 1 1 1 0 1 0
1 1 1 1 0 0 0
Notice that this resembles the one-dimensional problem. Let's sum the columns and see what we get:
1 2 2 2 0 1 0
Now, the problem is reduced to the one-dimensional case, with the exception that we must find a subsequence of contiguous j - i + 1 (which is 2 in this case) values. This means that each column in our j - i + 1 "window" must be full of ones. We can check for this efficiently by using the S matrix.
To understand how S works, consider a one-dimensional case again: let s[i] = sum of the first i elements of the vector a. Then what is the sum of the subsequence a[i..j]? It's the sum of all the elements up to and including a[j], minus the sum of all those up to and including a[i-1], meaning s[j] - s[i-1]. The 2d case works the same, except we have an s for each column.
I hope this is clear, if you have any more questions please ask.
I don't know if this fits your needs, but I think there's also an O(numLines*numCols) algorithm, based on dynamic programming. I can't figure it out yet, except for the case where the subarray you're after is square. Someone might have better insight however, so wait a bit more.
Define a new matrix A wich will store in A[i,j] two values: the width and the height of the largest submatrix with the left upper corner at i,j, fill this matrix starting from the bottom right corner, by rows bottom to top. You'll find four cases:
Perform these cases when given matrix at [i,j]=1
case 1: none of the right or bottom neighbour elements in the original matrix are equal to the current one, i.e: M[i,j] != M[i+1,j] and M[i,j] != M[i,j+1] being M the original matrix, in this case, the value of A[i,j] is 1x1
case 2: the neighbour element to the right is equal to the current one but the bottom one is different, the value of A[i,j].width is A[i+1,j].width+1 and A[i,j].height=1
case 3: the neighbour element to the bottom is equal but the right one is different, A[i,j].width=1, A[i,j].height=A[i,j+1].height+1
case 4: both neighbours are equal:
Three rectangles are considered:
A[i,j].width=A[i,j+1].width+1; A[i,j].height=1;
A[i,j].height=A[i+1,j].height+1; a[i,j].width=1;
A[i,j].width = min(A[i+1,j].width+1,A[i,j+1].width) and A[i,j].height = min(A[i,j+1]+1,A[i+1,j])
The one with the max area in the above three cases will be considered to represent the rectangle at this position.
The size of the largest matrix that has the upper left corner at i,j is A[i,j].width*A[i,j].height so you can update the max value found while calculating the A[i,j]
the bottom row and the rightmost column elements are treated as if their neighbours to the bottom and to the right respectively are different.
Here is a O(N) implementation in C#.
The idea is to use a dynamic programming to build an accumulated Matrix that has the size of the biggest submatrix including the current cell itself.
public static int LargestSquareMatrixOfOne(int[,] original_mat)
{
int[,] AccumulatedMatrix = new int[original_mat.GetLength(0), original_mat.GetLength(1)];
AccumulatedMatrix[0, 0] = original_mat[0, 0];
int biggestSize = 1;
for (int i = 0; i < original_mat.GetLength(0); i++)
{
for (int j = 0; j < original_mat.GetLength(1); j++)
{
if (i > 0 && j > 0)
{
if (original_mat[i, j] == 1)
{
AccumulatedMatrix[i, j] = Math.Min(AccumulatedMatrix[i - 1, j - 1], (Math.Min(AccumulatedMatrix[i - 1, j], AccumulatedMatrix[i, j - 1]))) + 1;
if (AccumulatedMatrix[i, j] > biggestSize)
{
biggestSize = AccumulatedMatrix[i, j];
}
}
else
{
AccumulatedMatrix[i, j] = 0;
}
}
else if ( (i > 0 && j == 0) || (j > 0 && i == 0))
{
if (original_mat[i, j] == 1) { AccumulatedMatrix[i, j] = 1; }
else { AccumulatedMatrix[i, j] = 0; }
}
}
}
return biggestSize;
}

Resources