Hungarian Algorithm: determining the minimum number of lines to cover all zeros - variable-assignment

I am trying to implement the Hungarian Algorithm. Check the Wikipedia steps: https://en.wikipedia.org/wiki/Hungarian_algorithm#Matrix_interpretation
Consider the following input (10 x 10):
x x 0 x 0 x 0 x x x
x x 0 x x x x x x x
0 0 x x x x x 0 x x
x x x x x 0 x x x x
x x x x x 0 x x x x
x x x 0 0 x x x x x
0 x x x x x x x x x
x x x x x x x x 0 0
x x x x x x x x x 0
x x x x x x x x 0 x
It requires 8 lines to cover all zeros (rows and cols indexed from 0): R0 R1 R2 R5 C0 C5 C8 C9.
However, follow the steps given in Wikepedia:
Tag the zeros as 'assigned' or 'cross-out'. Assigned: (0, 2), Crossed-out: (0, 4) (0, 6) (1, 2); Assigned: (2, 0), Crossed-out: (2, 1) (2, 7) (6, 0); Assigned: (3, 5), Crossed-out: (4, 5); Assigned: (5, 3), Crossed-out: (5, 4); Assigned: (7, 8), Crossed-out: (7, 9) (9, 8); Assigned: (8, 9).
Mark all rows having no assignments: R1 R4 R6 R9; mark all columns having zeros in newly marked rows: C2 C5 C0 C8; mark all rows having assignments in newly marked columns: R0 R3 R2 R7.
Repeat the last two steps in 2 until no row or column could be marked: mark all columns having zeros in newly marked rows: R0->C4 C6, R3->none, R2->C1 C7, R7->C9; mark all rows having assignments in newly marked columns: C4 C6 C1 C7->none, C9->R8.
Now all rows except for R5 have been marked; all columns except for C3 have been marked. The number of lines required to cover all zeros would be 9 vertical + 1 horizontal = 10, which is incorrect.
Could anyone tell where I have made a mistake? Or the algorithm to obtain minimum lines itself is inperfect?

Related

Fastest way to compute sum of a submatrix

What's the fastest way of computing the sum of a submatrix? Currently I'm going about this in the following way:
x = [1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4]
>> y = x(1:2, 1:2);
>> y
y =
1 2
1 2
>> sum(y(:))
ans =
6
>>
How can I compute the sum of all submatrices of x?
Edit
At each point (x,y) in the matrix, I want to compute the sum for a window of size 2M+1. For example, if I have a 4x4 matrix I want to compute the sums for 3x3 windows at each point where this is possible (e.g. it wouldn't be possible for the edges because the window would spill off the matrix)
for i = M:ncols-M
for j=M:nrows-M
end
end
Example
For a 4x4 matrix with a window size of 3, I would want these sums:
sum centered at (2,2)
+ + + x
+ + + x
+ + + x
x x x x
sum centered at (3,2)
x + + +
x + + +
x + + +
x x x x
sum centered at (2,3)
x x x x
+ + + x
+ + + x
+ + + x
sum centered at (3,3)
x x x x
x + + +
x + + +
x + + +
You're looking at computing a summed area table. Simply put, each element at row i and column j - (i,j) - in the output is the sum of the submatrix bounded from the top-left corner to the point of interest (i,j) which serves as the bottom right corner.
It's very simple with cumsum:
y = cumsum(cumsum(x, 1), 2);
Example:
>> x = repmat(1:4, 4, 1); %// your example
>> x
x =
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
... and the output:
>> y = cumsum(cumsum(x, 1), 2)
y =
1 3 6 10
2 6 12 20
3 9 18 30
4 12 24 40
As such, for your example, the submatrix sum from the top left corner to (i,j) = (2,2) is equal to 6.
If by "submatrix" you mean a sliding 2D-window of fixed size, that's essentially a 2D-convolution:
x = [1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4];
m = 2; n = 2; %// submatrix size
result = conv2(x, ones(m, n), 'valid');
In this example,
result =
6 10 14
6 10 14
6 10 14
This answer was created before clarifications by the OP. It assumes the submatrices do not overlap!
If you have the Image Processing Toolbox you can use blockproc:
A = blockproc(x,[2 2],#(k) sum(k.data(:)))
If you don't have it you can use accumarray:
n = 2; %// number of elements per block in row direction
N = size(x,1)/n; %// number of blocks in row direction
m = 2; %// number of elements per block in column direction
M = size(x,2)/m; %// number of blocks in column direction
idx = repelem( reshape(1:(N*M),N,M),n,m)
B = reshape( accumarray(x(:),idx(:)), N, M )
Some heavy reshaping will work too:
C = reshape(sum(reshape(permute(sum(reshape(x,n,m,[])),[2,3,1]).',n,[])),n,[]).'
A = B = C =
6 14
6 14
x = repmat(1:4, 4, 1); %// your example
[A,B] = size(x);
kk=1;
for ii = 1:2:A
for jj = 1:2:B
subsummed(kk) = sum(sum(x(ii:ii+1,jj:jj+1)));
kk = kk+1;
end
end
My looped version, in case you mean a "submatrix" in case of the four 2x2 matrices that make up your full one.

Design a simple matrix to group values from matrices

This problem is a succession of my previous problem:
1) Extract submatrices, 2) vectorize and then 3) put back
Now, I have two patients, named Ann and Ben.
Indeed the matrices A and B are data for Ann and the matrix C is data for Ben:
Now, I need to design a matrix M such that y = M*x where
y = [a11, a21, a12, a22, b11, b21, b12, b22]' which is a vector, resulting from concatenation of the top-left sub-matrices, Ann and Ben;
x = [2, 5, 4, 6, 7, 9, 6, 2, 9, 3, 4, 2]' which is a vector, resulting from concatenation of sub-matrices A, B and C.
Here, the M is a 8 by 12 matrix that
a11 = 2 + 7, a21 = 5 + 9, .., a22 = 6 + 2 and b11 = 9, ..b22 = 2.
I design the M manually by:
M=zeros(8,12)
M(1,1)=1; M(1,5)=1; % compute a11
M(2,2)=1; M(2,6)=1; % compute a21
M(3,3)=1; M(3,7)=1; % compute a12
M(4,4)=1; M(4,8)=1; % compute a22
M(5,9)=1; % for setting b11 = 9, C(1,1)
M(6,10)=1; % for setting b21 = 3, C(2,1)
M(7,11)=1; % for setting b12 = 4, C(1,2)
M(8,12)=1 % for setting b22 = 2, C(2,2)
Obviously, in general for M(i,j), i means the 8 linear-index position of vector y and j means linear-index position of vector x.
However, I largely simplified my original problem that I want to construct this M automatically.
Thanks in advance for giving me a hand.
Here you have my solution. I have essentially build the matrix M automatically (from the proper indexes) as you suggested.
A = [2 4 8;
5 6 3;
10 3 6];
B = [7 6 3;
9 2 9;
10 2 3];
C = [9 4 7;
3 2 5;
10 3 4];
% All matrices in the same array
concat = cat(3, A, B, C);
concat_sub = concat(1:2,1:2,:);
x = concat_sub(:);
n = numel(x)/3; %Number of elements in each subset
M2 = zeros(12,8); %Transpose of the M matrix (it could be implemented directly over M but that was my first approach)
% The indexes you need
idx1 = 1:13:12*n; % Indeces for A
idx2 = 5:13:12*2*n; % Indices for B and C
M2([idx1 idx2]) = 1;
M = M2';
y = M*x
I have taken advantage of the shape that the matrix M shold take:
You can index into things and extract what you want without multiplication. For your example:
A = [2 4 8; 5 6 3; 10 3 6];
B = [7 6 3; 9 2 9; 10 2 3];
C = [9 4 7;3 2 5; 10 3 4];
idx = logical([1 1 0;1 1 0; 0 0 0]);
Ai = A(idx);
Bi = B(idx);
Ci = C(idx);
output = [Ai; Bi; Ci];
y = [Ai + Bi; Ci]; % desired y vector
This shows each step individually but they can be done in 2 lines. Define the index and then apply it.
idx = logical([1 1 0;1 1 0;0 0 0]);
output = [A(idx); B(idx); C(idx)];
y = [Ai + Bi; Ci]; % desired y vector
Also you can use linear indexing with idx = [1 2 4 5]' This will produce the same subvector for each of A B C. Either way works.
idx = [1 2 4 5]';
or alternatively
idx = [1;2;4;5];
output = [A(idx); B(idx); C(idx)];
y = [Ai + Bi; Ci]; % desired y vector
Either way works. Check out doc sub2ind for some examples of indexing from MathWorks.

remove rows of matrix in matlab

If I have a matrix X with only one column and it has some negative values in some rows. How can I remove only the negative values?
Example:
X=[-1; 2; 3; -4; 5]
should become:
X=[2; 3; 5]
Also, how can I remove values from
y=[1; 2; 3; 4; 5]
based on where the negative values in X are found? y should be [2; 3; 5] after this operation.
Removing negative values from X:
You can either reassign X to a vector which only contains the values of X which are not negative:
>> X = X(X>=0)
X =
2
3
5
or delete the negative values from X:
>> X(X<0) = []
X =
2
3
5
Removing values from y based on the indices of negative values in X is similar. Either reassign:
>> y = y(X>=0)
y =
2
3
5
Or delete:
>> y(X<0) = []
y =
2
3
5
If you want to modify both vectors based on the negative values in X remember to do the operation to y first or store a logical vector for the positions where X<0. For example:
>> ind = X < 0;
>> X(ind) = []
X =
2
3
5
>> y(ind) = []
y =
2
3
5

How to Select a column based on string

So I have a Cell say its 5x5
1 2 3 4 5
1 A B C D E
2 X X X X X
3 X X X X X
4 X X X X X
5 X X X X X
In Matlab how do I select a column based on the string in the first row. Say I want all the values in the C column but I dont have the index of C.
mycell(:,strcmp(mycell(1,:),'C'))

All combinations for matrix in matlab

I'm trying find all combinations of an n-by-n matrix without repetitions.
For example, I have a matrix like this:
A = [321 319 322; ...
320 180 130; ...
299 100 310];
I want the following result:
(321 180 310)
(321 130 100)
(319 320 310)
(319 139 299)
(322 320 100)
(322 180 299)
I have tried using ndgrid, but it takes the row or the column twice.
Here's a simpler (native) solution with perms and meshgrid:
N = size(A, 1);
X = perms(1:N); % # Permuations of column indices
Y = meshgrid(1:N, 1:factorial(N)); % # Row indices
idx = (X - 1) * N + Y; % # Convert to linear indexing
C = A(idx) % # Extract combinations
The result is a matrix, each row containing a different combination of elements:
C =
321 180 310
319 320 310
321 130 100
319 130 299
322 320 100
322 180 299
This solution can also be shortened to:
C = A((perms(1:N) - 1) * N + meshgrid(1:N, 1:factorial(N)))
ALLCOMB is the key to your question
E.g. I am not front of a MATLAB machine so, I took a sample from web.
x = allcomb([1 3 5],[-3 8],[],[0 1]) ;
ans
1 -3 0
1 -3 1
1 8 0
...
5 -3 1
5 8 0
5 8 1
You can use perms to permute the columns as follows:
% A is given m x n matrix
row = 1:size( A, 1 );
col = perms( 1:size( A, 2 ) );
B = zeros( size( col, 1 ), length( row )); % Allocate memory for storage
% Simple for-loop (this should be vectorized)
% for c = 1:size( B, 2 )
% for r = 1:size( B, 1 )
% B( r, c ) = A( row( c ), col( r, c ));
% end
% end
% Simple for-loop (further vectorization possible)
r = 1:size( B, 1 );
for c = 1:size( B, 2 )
B( r, c ) = A( row( c ), col( r, c ));
end