Rearranging rows of a boolean matrix so that the diagonals are all non-zero if possible

15 views (last 30 days)
Hello, just as a disclaimer I have a question about improving my code, not on how to get it to work.
I wrote a code that rearranges just the rows of a boolean matrix so that the diagonal values are all non-zero. This code gets pretty slow as the matrix being fed in gets pretty large. It starts lagging pretty bad with 25x25 matrices. I'm curious what I can do to speed my code up.
Here is the function that I have right now:
function [reAdmis, unmatchedTaskList] = rearrange(admis)
[n,~] = size(admis);
potAssignment = zeros(1,n);%potential assignments
dList=1:n; %Decision list: elements are zeroed if chosen
reAdmis = zeros(n,n); % reAdmis will be the answer
AdmisTemp = admis;
CC = sum(admis,1); %column count
RC = sum(admis,2); %row count
[~, colIndex] = sort(CC);
[~, rowIndex] = sort(RC);
[reAdmisTemp, dIndex] = reorganizeRow(AdmisTemp, rowIndex, n); %reorganizes the rows based on RC
for i = 1:n
for j = 1:n
if reAdmisTemp(j,colIndex(i))
reAdmis(colIndex(i),:)=admis(rowIndex(j),:);
AdmisTemp(rowIndex(j),:)=zeros; %used row gets zeroed
dList(dIndex(j))=0; % chosen element in dList is zeroed
potAssignment(dIndex(j))=colIndex(i); %Potential assignment is updated
AdmisTemp(:,colIndex(i))=zeros; %used column gets zeroed
break
end
end
CC = sum(AdmisTemp,1);
RC = sum(AdmisTemp,2);
[~, colIndex] = sort(CC);
[~, rowIndex] = sort(RC);
[reAdmisTemp, dIndex] = reorganizeRow(AdmisTemp, rowIndex, n);
end
end
function [organized, dIndex] = reorganizeRow(A, dIndex, n)
organized = zeros(n,n);
for i = 1:n
organized(i,:)=A(dIndex(i),:);
end
end
An example boolean matrix to use and its output could be:
admis = [1 0 1 0 0
0 0 0 0 1
1 1 0 0 1
0 1 0 1 1
1 1 1 1 1]
reAdmis = [1 1 0 0 1
0 1 0 1 1
1 0 1 0 0
1 1 1 1 1
0 0 0 0 1]
My hope is that this can be as fast as possible. I have no hard number for a time requirement, however my clocked speed using tic-toc was about 0.0003 seconds, which I'd like to be able to beat.
Thank you in advance for any help that you are able to provide
  4 Comments
Matt J
Matt J on 16 Jul 2020
Edited: Matt J on 16 Jul 2020
Yes, but how large are you hoping to make the matrix? Would it get much larger than 25x25?

Sign in to comment.

Answers (2)

Bruno Luong
Bruno Luong on 16 Jul 2020
Your problem belong to the so-called "Assignment problem", that has dedicated algorithm called Hungarian assigment.
Take a look on the formal definition, try to understand it then find some implementation on File Exchange.
  6 Comments

Sign in to comment.


Matt J
Matt J on 11 Jul 2020
When your version works, it often works faster than the version below, however your version doesn't always get it right. I've attached a .mat file with an example matrix admis for which your version does not manage to get all non-zero diagonal elements.
load admis700 admis
N=size(admis,1);
P=optimvar('P',[N,N],'LowerBound',0,'UpperBound',1,'Type','integer');
prob=optimproblem('ObjectiveSense','max');
prob.Constraints.row=sum(P,1)==ones(1,N);
prob.Constraints.col=sum(P,2)==ones(N,1);
E=speye(N);
opts=optimoptions('intlinprog','Display','none');
tic;
prob.Objective=sum(sum((P*admis).*E));
sol=solve(prob,'options',opts);
reAdmis=round(sol.P)*admis;
toc;
  6 Comments
2Lindsay2
2Lindsay2 on 18 Jul 2020
Thank you! I notice though that there is no guarantee that the sparse matrix that gets produced actually has a solution where all the diagonal elements are one. When there is not a solution where all the diagonal elements are one my code stops inserting the rows into the empty matrix "reAdmis", which is why the results would be different. When you tested this did you verify there was a solution where all the diagonal elements were one?
Matt J
Matt J on 18 Jul 2020
Edited: Matt J on 18 Jul 2020
The randomization procedure that I used to explore different choices of admis did not ensure this, however, the example failure cases admis10.mat and admis700.mat that I provided for you did have solutions where all the diagonal elements are one, as you will see when you run either my code or Bruno's.
In any case, I do not think there is any longer any reason for you to focus on either my solution or your own. Bruno's solution works and, for large N, is the fastest among all 3 methods. To demonstrate, I attach a 3rd data example, for which your solution does happen to work:
load admis200
tic;
R1=Lindsay(admis);
toc;
tic
R2=MattJ(admis);
toc
tic
R3=Bruno(admis);
toc
Tr1=trace(R1)
Tr2=trace(R2)
Tr3=trace(R3)
results in
Elapsed time is 0.030986 seconds.
Elapsed time is 0.135317 seconds.
Elapsed time is 0.015373 seconds.
Tr1 =
200
Tr2 =
200
Tr3 =
200

Sign in to comment.

Categories

Find more on Operating on Diagonal Matrices in Help Center and File Exchange

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!