Need help in creating a matrix with exactly one 1 in each row/column at different locations - matlab

This code generates a matrix having 1 in each row and this 1 may be on same locations in rows.
I want for each row the location of 1 must be different i.e 1 must not overlap in columns.

Suppose you want the result matrix to be of size [m, n].
First, randomly select , for each column, the index of the 1
idx = randi(m, 1, n);
Second, allocate an all-zero matrix of size [m,n]
res = zeros(m,n);
Finally, set the corresponding entries to one:
res( sub2ind([m,n], idx, 1:n) ) = 1;
An example result for [3,4] matrix:
res =
0 0 1 0
0 0 0 0
1 1 0 1

Related

How to find first '1' in every row in MATLAB

I have a matrix,
A = [ 0 0 0 0 0 0 1 1 1 1 0 0; 0 0 0 0 0 1 1 1 1 0 0 0; 0 0 0 0 0 0 1 1 1 1 0 0]
My question is, how to find the first '1' in each row. I want the output will show like this:
B = [7; 6; 7]
Meaning that, for the first row, the number 1 found on column number 7, second row found in column number 6 and so on.
You can use the second output of max, which gives the position of the maximum:
v = 1; % desired value
[~, B] = max(A==v, [], 2); % position of maxima along the second dimension
As a bonus, if there can be rows that don't contain the desired value, you can output 0 for those rows as follows:
[m, B] = max(A==v, [], 2);
B = B.*m;
Find cumulative sum of each row of A and use find to get the row and column subscripts of ones and then order the column subscripts according to rows to get the desired matrix B.
[rind,cind] = find(cumsum(A,2)==1);
[~, rind] = unique(rind);
B = cind(rind);

How to efficiently select only certain columns of a triangular matrix

I have the following problem:
I need certain columns of a huge triangular 1-0 matrix.
E.g.
Matrix =
1 0 0 0
1 1 0 0
1 1 1 0
1 1 1 1
Index =
[1 4]
Result =
1 0
1 0
1 0
1 1
I figured the easiest way would be:
index = [10 20 300] %arbitrary index
buf = tril(ones(60000,60000))
matr = buf(:,index)
However, this does not work as the buffer matrix is too large and leads to MATLAB throwing an error. Thus, this approach is blocked.
How can I solve that problem efficiently? (E.g. it would be trivial by just looping over the index array and concatenating self-made rows, however this would be slow and I was hoping for a faster approach)
The index array will not be larger than 1/10th of the available columns.
If the matrix contains ones on the main diagonal and below, and zeros otherwise, you can do it as follows without actually generating the matrix:
N = 10; % number of rows of (implicit) matrix
Index = [1 4]; % column indices
Result = bsxfun(#ge, (1:N).', Index);

return the index of the last K non-zero element of each row of a matrix

Is there a vectorization way of returning the index of the last K nonzero elements of each row of a matrix?
For example, my matrix only contains 0 and 1 and the last column of each row is always 1. Then I want to find the index of the last K, where K>1, nonzero elements of each row. If a row only has M (less than K) nonzero elements, then the index for that row is just the index of the last M nonzero element. e.g.
A = [0 1 0 1;
1 1 0 1;
1 1 1 1;
0 0 0 1]
And my K = 2, then I expected to return a matrix such that
B = [0 1 0 1;
0 1 0 1;
0 0 1 1;
0 0 0 1]
Namely B is originally a zero matrix with same shape as A, then it copies each row of A where the corresponding column starts from the index of the last K non-zero element of the row of A (and if in one row of A there is only M < K non-zero element, then it starts from the index of the last M non-zero element of that row of A)
Knowing that elements are only 0 or 1, you can make a mask using cumsum on the flipped matrix A and throw away values with a cumulative sum greater than k:
A = [0 1 0 1;1 1 0 1;1 1 1 1;0 0 0 1]
k = 2;
C = fliplr(cumsum(fliplr(A), 2)); % take the cumulative sum backwards across rows
M = (C <= k); % cumsum <= k includes 0 elements too, so...
B = A .* M % multiply original matrix by mask
As mentioned in the comments (Thanks #KQS!), if you're using a recent version of MATLAB, there's a direction optional parameter to cumsum, so the line to generate C can be shortened to:
C = cumsum(A, 2, 'reverse');
Results:
A =
0 1 0 1
1 1 0 1
1 1 1 1
0 0 0 1
B =
0 1 0 1
0 1 0 1
0 0 1 1
0 0 0 1
knowing that find function can get indices of last k elements, we can use bsxfun to apply find to rows of a matrix to find which element in each row satisfy the condition. find again used to extract rows and columns of nonzero elements of the resultant matrix, so reducing size of data and complexity of operations. then save the result to a sparse matrix then convert to full matrix:
A = [0 1 0 1;
1 1 0 1;
1 1 1 1;
0 0 0 1]
k = 2;
[row , col]= size(A);
last_nz = bsxfun(#(a,b)find(a,b,'last'),A',(repmat(k, 1, row))); %get indices of last two nonzero elements for each row
[~,rr,cc]=find(last_nz); %get columns and rows of correspondong element for whole matrix
B = full(sparse(rr,cc,1));

Finding the column index for the 1 in each row of a matrix

I have the following matrix in Matlab:
M = [0 0 1
1 0 0
0 1 0
1 0 0
0 0 1];
Each row has exactly one 1. How can I (without looping) determine a column vector so that the first element is a 2 if there is a 1 in the second column, the second element is a 3 for a one in the third column etc.? The above example should turn into:
M = [ 3
1
2
1
3];
You can actually solve this with simple matrix multiplication.
result = M * (1:size(M, 2)).';
3
1
2
1
3
This works by multiplying your M x 3 matrix with a 3 x 1 array where the elements of the 3x1 are simply [1; 2; 3]. Briefly, for each row of M, element-wise multiplication is performed with the 3 x 1 array. Only the 1's in the row of M will yield anything in the result. Then the result of this element-wise multiplication is summed. Because you only have one "1" per row, the result is going to be the column index where that 1 is located.
So for example for the first row of M.
element_wise_multiplication = [0 0 1] .* [1 2 3]
[0, 0, 3]
sum(element_wise_multiplication)
3
Update
Based on the solutions provided by #reyryeng and #Luis below, I decided to run a comparison to see how the performance of the various methods compared.
To setup the test matrix (M) I created a matrix of the form specified in the original question and varied the number of rows. Which column had the 1 was chosen randomly using randi([1 nCols], size(M, 1)). Execution times were analyzed using timeit.
When run using M of type double (MATLAB's default) you get the following execution times.
If M is a logical, then the matrix multiplication takes a hit due to the fact that it has to be converted to a numerical type prior to matrix multiplication, whereas the other two have a bit of a performance improvement.
Here is the test code that I used.
sizes = round(linspace(100, 100000, 100));
times = zeros(numel(sizes), 3);
for k = 1:numel(sizes)
M = generateM(sizes(k));
times(k,1) = timeit(#()M * (1:size(M, 2)).');
M = generateM(sizes(k));
times(k,2) = timeit(#()max(M, [], 2), 2);
M = generateM(sizes(k));
times(k,3) = timeit(#()find(M.'), 2);
end
figure
plot(range, times / 1000);
legend({'Multiplication', 'Max', 'Find'})
xlabel('Number of rows in M')
ylabel('Execution Time (ms)')
function M = generateM(nRows)
M = zeros(nRows, 3);
col = randi([1 size(M, 2)], 1, size(M, 1));
M(sub2ind(size(M), 1:numel(col), col)) = 1;
end
You can also abuse find and observe the row positions of the transpose of M. You have to transpose the matrix first as find operates in column major order:
M = [0 0 1
1 0 0
0 1 0
1 0 0
0 0 1];
[out,~] = find(M.');
Not sure if this is faster than matrix multiplication though.
Yet another approach: use the second output of max:
[~, result] = max(M.', [], 1);
Or, as suggested by #rayryeng, use max along the second dimension instead of transposing M:
[~, result] = max(M, [], 2);
For
M = [0 0 1
1 0 0
0 1 0
1 0 0
0 0 1];
this gives
result =
3 1 2 1 3
If M contains more than one 1 in a given row, this will give the index of the first such 1.

How can I vectorise this loop in MATLAB

I have a loop that iterates over a matrix and sets all rows and columns with only one non-zero element to all zeroes.
so for example, it will transform this matrix:
A = [ 1 0 1 1
0 0 1 0
1 1 1 1
1 0 1 1 ]
to the matrix:
A' = [ 1 0 1 1
0 0 0 0
1 0 1 1
1 0 1 1 ]
row/column 2 of A only has 1 non zero element in it, so every element in row/column 2 is set to 0 in A'
(it is assumed that the matrices will always be diagonally symmetrical)
here is my non-vectorised code:
for ii = 1:length(A)
if nnz(A(ii,:)) == 1
A(ii,:) = 0;
A(:,ii) = 0;
end
end
Is there a more efficient way of writing this code in MATLAB?
EDIT:
I have been asked in the comments for some clarification, so I will oblige.
The purpose of this code is to remove edges from a graph that lead to a vertex of degree 1.
if A is the adjacency matrix representing a undirected graph G, then a row or column of that matrix which only has one non-zero element indicates that row/column represents a vertex of degree one, as it only has one edge incident to it.
My objective is to remove such edges from the graph, as these vertices will never be visited in a solution to the problem I am trying to solve, and reducing the graph will also reduce the size of the input to my search algorithm.
#TimeString, i understand that in the example you gave, recursively applying the algorithm to your matrix will result in a zero matrix, however the matrices that I am applying it to represent large, connected graphs, so there will never be a case like that. In response to your question as to why I only check for how many elements in a row, but the clear both columns and rows; this is because the matrix is always diagonally symmetrical, so i know that if something is true for a row, so it will be for the corresponding column..
so, just to clarify using another example:
I want to turn this graph G:
represented by matrix:
A = [ 0 1 1 0
1 0 1 0
1 1 0 1
0 0 1 0 ]
to this graph G':
represented by this matrix:
A' = [ 0 1 1 0
1 0 1 0
1 1 0 0
0 0 0 0 ]
(i realise that this matrix should actually be a 3x3 matrix because point D has been removed, but i already know how to shrink the matrix in this instance, my question is about efficiently setting columns/rows with only 1 non-zero element all to 0)
i hope that is a good enough clarification..
Not sure if it's really faster (depends on Matlab's JIT) but you can try the following:
To find out which columns (equivalently, rows, since the matrix is symmetric) have more than one non zero element use:
sum(A ~= 0) > 1
The ~= 0 is probably not needed in your case since the matrix consists of 1/0 elements only (graph edges if I understand correctly).
Transform the above into a diagonal matrix in order to eliminate unwanted columns:
D = diag(sum(A~=0) > 1)
And multiply with A from left to zero rows and from right to zero columns:
res = D * A * D
Thanks to nimrodm's suggestion of using sum(A ~= 0) instead of nnz, i managed to find a better solution than my original one
to clear the rows with one element i use:
A(sum(A ~= 0) == 1,:) = 0;
and then to clear columns with one element:
A(:,sum(A ~= 0) == 1) = 0;
for those of you who are interested, i did a 'tic-toc' comparison on a 1000 x 1000 matrix:
% establish matrix
A = magic(1000);
rem_rows = [200,555,950];
A(rem_rows,:) = 0;
A(:,rem_rows) = 0;
% insert single element into empty rows/columns
A(rem_rows,500) = 5;
A(500,rem_rows) = 5;
% testing original version
A_temp = A;
for test = 1
tic
for ii = 1:length(A_temp)
if nnz(A_temp(ii,:)) == 1
A_temp(ii,:) = 0;
A_temp(:,ii) = 0;
end
end
toc
end
Elapsed time is 0.041104 seconds.
% testing new version
A_temp = A;
for test = 1
tic
A_temp(sum(A_temp ~= 0) == 1,:) = 0;
A_temp(:,sum(A_temp ~= 0) == 1) = 0;
toc
end
Elapsed time is 0.010378 seconds
% testing matrix operations based solution suggested by nimrodm
A_temp = A;
for test = 1
tic
B = diag(sum(A_temp ~= 0) > 1);
res = B * A_temp * B;
toc
end
Elapsed time is 0.258799 seconds
so it appears that the single line version that I came up with, inspired by nimrodm's suggestion, is the fastest
thanks for all your help!
Bsxfuning it -
A(bsxfun(#or,(sum(A~=0,2)==1),(sum(A~=0,1)==1))) = 0
Sample run -
>> A
A =
1 0 1 1
0 0 1 0
1 1 1 1
1 0 1 1
>> A(bsxfun(#or,(sum(A~=0,2)==1),(sum(A~=0,1)==1))) = 0
A =
1 0 1 1
0 0 0 0
1 0 1 1
1 0 1 1