Obtaining the position of a random 1 in an array of 0's in MATLAB - matlab

I have a matrix which is initialized as follows:
stateAndAction = zeros(11, 4);
Over time the matrix will be updated so that at a given index, there will be a one. So at any given time we could have something that looks like this
1 1 0 1
1 0 0 0
0 1 0 0
0 0 1 0
0 1 0 0
0 0 0 1
1 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
How do I find a random row and column with a one in it?
This is the function signature I had in mind:
[random_row_index, random_column_index] = findRandom(stateAndAction)

You can find the position of non-zero elements with find, select a random element and convert index to row/column position in the array:
function [random_row_index, random_column_index] = findRandom(stateAndAction)
ids = find(stateAndAction==1);
random = randi([1,numel(ids)],1);
id=ids(random);
[random_row_index, random_column_index] = ind2sub(size(stateAndAction),id);
end

Related

How to randomly select x number of indices from a matrix in Matlab

I'm trying to generate a randomly scattered but limited number of 1's in a matrix of zeros efficiently.
Say I have a 10x10 matrix of zeros (zeros(10)) and I want to randomly place ten 1's so it looks like:
0 0 0 0 0 0 0 0 0 1
0 0 0 0 0 1 0 0 0 0
0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 1 0
1 0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0
0 1 0 0 0 0 0 1 0 0
0 0 0 0 1 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0
How can I do this WITHOUT a for-loop and without manually plugging in each position (this example is a much smaller version of my real problem)?
My code so far:
% Generate zeros
M = zeros(10)
% Generate random indices
Rands = [randsample(10, 10) randsample(10, 10)]
Where the first column is intended to be the row indices and the second column the column indices.
Now I obviously can't just drop these indices into the row and column indices of M like this:
M(Rands(:,1), Rands(:,2)) = 1
How can I vecorise the changes to these random indices?
You can use randperm to randomly generate the linear indices to be filled with 1:
sz = [10 10]; % desired size
n = 10; % desired number of ones
M = zeros(sz);
M(randperm(prod(sz), n)) = 1;
Alternatively, you can use randperm and reshape in one line:
M = reshape(randperm(prod(sz))<=n, sz);
You can use sub2ind to convert subscripts to linear index:
M(sub2ind(size(M),Rands(:,1),Rands(:,2)))=1

Changing the diagonals beside center diagonal of matrix

Is there a quick way to change the diagonals beside the center diagonal (referring to the 1s below):
m =
2 1 0 0 0 0 0 0 0
1 2 1 0 0 0 0 0 0
0 1 2 1 0 0 0 0 0
0 0 1 2 1 0 0 0 0
0 0 0 1 2 1 0 0 0
0 0 0 0 1 2 1 0 0
0 0 0 0 0 1 2 1 0
0 0 0 0 0 0 1 2 1
0 0 0 0 0 0 0 1 2
A quick way to change the center diagonal is m(logical(eye(size(m)))) = 2. How about assigning the diagonals beside it to values of 1?
The diag function takes a second parameter, k, which specifies which diagonal to target:
diag([-1,-1,-1,-1],-1) % or diag(-1*ones(4,1),1)
ans =
0 0 0 0 0
-1 0 0 0 0
0 -1 0 0 0
0 0 -1 0 0
0 0 0 -1 0
diag([1,1,1,1],1)
ans =
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1
0 0 0 0 0
diag([2,2,2],2)
ans =
0 0 2 0 0
0 0 0 2 0
0 0 0 0 2
0 0 0 0 0
0 0 0 0 0
If you already have an existing matrix and you want to change one of the diagonals you could do this:
M = magic(5) % example matrix
v = [1,2,3,4] % example vector that must replace the first diagonal of M, i.e. the diagonal one element above the main diagonal
M - diag(diag(M,1),1) + diag(v,1)
The idea is to first use diag to extract the numbers of the diagonal you want to change, diag(M,1). Then to use diag again to change the vector that the first call to diag created into a matrix, diag(diag(M,1),1). You'll notice that this creates a matrix with the same dimensions as M, the same numbers as M on the 1st diagonal and 0s everywhere else. Thus M - diag(diag(M,1),1) just sets that first diagonal to 0. Now diag(v,1) creates a matrix with the same dimensions as M that is 0 everywhere but with the numbers of v on the first diagonal and so adding diag(v,1) only affects that first diagonal which is all 0s thanks to -diag(diag(M,1),1)
An alternative if you are just applying a constant to a diagonal (for example setting all the values on the first diagonal below the main diagonal to 6):
n = 5;
k = -1;
a = 6;
M = magic(n);
ind = diag(true(n-abs(k),1),k);
M(ind) = a;

Matlab Generating a Matrix with random elements

How can I generate a Matrix with Boolean elements, but the sum of each row is equal to a certain constant number.
Is each row the same one number?
k = 5;
m = 10;
n = 10;
[~, I] = sort(rand(m,n), 2)
M = I <= k
If you don't want the same number of 1s in each row, but rather have a vector that specifies per row how many 1s you want then you need to use bsxfun as well:
K = (1:10)'; %//'
m = 10;
n = 10;
[~, I] = sort(rand(m,n), 2)
M = bsxfun(#ge, K,I)
Lets say you want to have 20 columns (n=20) and your vector a contains the number of ones you want in each row:
n=20;
a= [5 6 1 9 4];
X= zeros(numel(a),n);
for k=1:numel(a)
rand_order=randperm(n);
row_entries=[ones(1,a(k)),zeros(1,n-a(k))];
row_entries=row_entries(rand_order);
X(k,:)=row_entries;
end
X=boolean(X);
What I do is generate me a random ordered index array rand_order then getting an array which contains the wanted number of ones filled with zero. Reorder those elements according to rand_order saving it and converting it to logical. And because of the use of a for loop rand_order is all the time computed again, so giving you different locations for your output:
1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 1 1 0 0
0 0 0 1 0 0 0 1 1 0 1 0 0 0 0 0 1 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
1 0 0 1 0 1 1 0 1 0 0 1 1 0 0 0 1 1 0 0
1 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0

Create coordinate array from adjacency matrix - Matlab

I have a quick graph theory question.
I have a 13 x 13 adjacency matrix in Matlab. I've already taken just the lower diagonal (this is an undirected graph) and subtracted off the identity matrix (so there aren't edges joining a node to itself). I then added a column on the left and a row on the top with the ID numbers of the four nodes. The resulting 14 x 14 adjacency matrix looks like this:
A =
0 1 1 1 1 2 2 2 3 3 3 4 4 4
1 0 0 0 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0 0 0 0 0
2 1 0 0 0 0 0 0 0 0 0 0 0 0
2 0 0 0 0 0 0 0 0 0 0 0 0 0
2 0 0 0 0 0 0 0 0 0 0 0 0 0
3 0 0 1 0 0 0 0 0 0 0 0 0 0
3 0 0 0 0 0 0 0 0 0 0 0 0 0
3 0 1 0 0 0 0 0 0 0 0 0 0 0
4 1 0 0 0 1 0 0 0 0 0 0 0 0
4 0 0 0 1 0 0 0 0 0 0 0 0 0
4 0 0 0 0 0 0 1 0 0 0 0 0 0
How can I create the coordinate array from this? I know the result should have 7 rows (one for each unique node pair).
Please let me know if you can help. Thanks in advance!
We can use the node IDs that are provided at the first row and first column to help us create a node adjacency list. What we need to do is split up the variables so that we separate out the first row, called rowList and the first column colList. We will denote these the row and column lists respectively. We will also extract the adjacency matrix which is the rest of the matrix. The basic algorithm is the following:
Find those rows and columns that are non-zero in the adjacency matrix.
Use these rows and columns to index the corresponding rows and columns lists and spit out a co-ordinate array.
Without further ado:
rowList = A(2:end,1);
colList = A(1,2:end).';
AdjMatr = A(2:end,2:end);
[nonZeroRows, nonZeroCols] = find(AdjMatr);
nodeList = [rowList(nonZeroRows) colList(nonZeroCols)];
The output thus gives:
nodeList =
2 1
4 1
3 1
3 1
4 1
4 2
4 2
This answer does not give unique rows of course, and produces duplicates. If you wish to have unique rows, consider doing:
nodeListUnique = unique(nodeList, 'rows');
The output is:
nodeListUnique =
2 1
3 1
4 1
4 2
It appears that what you want is:
[ii, jj] = find(A(2:end,2:end)); %// row and col indices of ones in inner matrix
result = [ A(ii+1,1) A(1,jj+1).' ]; %'// node numbers corresponding to ii and jj
In your example, this gives
result =
2 1
4 1
3 1
3 1
4 1
4 2
4 2
If you need unique rows:
result = unique(result, 'rows');
which gives
result =
2 1
3 1
4 1
4 2

Using find function on columns and rows in matlab

I am having some problems with the find function in MATLAB. I have a matrix consisting of zeros and ones (representing the geometry of a structural element), where material is present when the matrix element = 1, and where no material is present when the matrix element = 0. The matrix may have the general form shown below (it will update as the geometry is changed, but that isn't too important).
Geometry = [0 0 0 0 0 0 0 0 0 0;
0 0 1 0 1 0 1 1 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 0 0 0 0 0 0 0;
0 0 0 0 0 0 0 1 0 0;
0 0 0 0 0 0 0 0 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 1 1 1 0 1 0 0;
0 0 0 0 0 0 0 0 0 0;]
I'm trying to find the the rows and columns that are not continuously connected (i.e. where the row and columns are not all equal to 1 between the outer extents of the row or column) and then update them so they are all connected. I.e. the matrix above becomes:
Geometry = [0 0 0 0 0 0 0 0 0 0;
0 0 1 1 1 1 1 1 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 1 1 1 1 1 0 0;
0 0 0 0 0 0 0 0 0 0;]
The problem I am having is I want to be able to find the indices of the first and last element that is equal to 1 in each row (and column), which will then be used to update the geoemtry matrix.
Ideally, I want to represent these in vectors, so going across the columns, find the row number of the first element equal to 1 and store this in a vector called rowfirst.
I.e.:
rowfirst = zeros(1,numcols)
for i = 1:numcols % Going across the columns
rowfirst(i) = find(Geometry(i,1) == 1, 1,'first')
% Store values in vector called rowfirst
end
and the repeat this for the columns and to find the last elements in each row.
For some reason, I can't get the values to store properly in the vector, does anyone have an idea of where I'm going wrong?
Thanks in advance. Please let me know if that isn't clear, as I may not have explained the problem very well.
0) bwmorph(Geometry,'close') dose it all in one line. If the holes may be bigger, try bwmorph(Geometry,'close',Inf).
Regarding your attempt:
1) It should be Geometry(i,:) instead of Geometry(i,1).
2) Your real problem here is empty matrices. Actually, what do you want rowfirst(i) to be if there are no 1s in the i'th row?
Ok, I can spot two mistakes:
You should use an array as the first argument of find. So, if you want to find the row number of the first element of each column, then you should use find(Geometry(:, i), 1, 'first').
Find returns an empty array if the column contains only zeros. You should handle this case and decide what number you want to put into rownumber (e.g. you can put -1, to indicate that the corresponding column contains no non-zero elements).
Following the above, you can try this:
for i = 1:numcols
tmp = find(Geometry(:, i), 1, 'first');
if(tmp)
rowfirst(i) = tmp;
else
rowfirst(i) = -1;
end;
end;
I'm pretty sure there's a more efficient way of doing this, but if you replace your call to find with this, it should work ok:
find(Geometry(i,:), 1,'first')
(otherwise you're just looking at the first cell of the ith row. And the == 1 is useless, since find already returns only non-zero elements, and your matrix is binary)
Use the AccumArray() function to find the min and max col (row) number.
Imagine finding the last (first) row in each column that contains a NaN.
a = [1 nan nan nan ;
2 2 3 4;
3 nan 3 3;
4 nan 4 4]
This code gets the row indices for the last NaN in each column.
[row,col] = find(isnan(a))
accumarray(col,row,[],#max)
This code gets the row indices for the first NaN in each column.
[row,col] = find(isnan(a))
accumarray(col,row,[],#min)
Swap the row and col variables to scan row-wise instead of column-wise.
This answer inspired by Finding value and index of min value in a matrix, grouped by column values