How can one find the number of separated linked blocks using a symmetric matrix of 0s and 1s in Matlab?
For example in matrix A, if A(n,m)=1, member n and m are connected. Connected elements make blocks. In below matrix, members 2,3,4,5,6,8,9 are connected and make a block. Also, there are two clusters of size equal to 2 and one block of size 7.
A = [1 0 0 0 0 0 0 0 0 0
0 1 1 1 1 1 0 1 1 0
0 1 1 1 1 1 0 1 1 0
0 1 1 1 1 1 0 1 1 0
0 1 1 1 1 1 0 1 1 0
0 1 1 1 1 1 0 1 1 0
0 0 0 0 0 0 1 0 0 0
0 1 1 1 1 1 0 1 1 0
0 1 1 1 1 1 0 1 1 0
0 0 0 0 0 0 0 0 0 1]
following your previous question (link) you can use labeling instead of binary indications and then sqrt the number of members in each block:
A = false(10);
% direct connections
A(2,3) = 1;
A(3,4) = 1;
A(5,6) = 1;
A(4,9) = 1;
A = A | A';
B = double(A | diag(ones(1,10))); % each node is connected to it self
B(B == 1) = 1:nnz(B); % set initial unique labels
while true
B_old = B;
% connect indirect connected nodes
for node = 1:size(B,1)
row = B(node,:);
col = row';
row = row > 0;
col(col > 0) = max(col);
cols = repmat(col,[1 nnz(row)]);
% set the same label for each group of connected nodes
B(:,row) = max(B(:,row) , cols);
end
if isequal(B,B_old)
break
end
end
% get block size
u = unique(B);
counts = hist(B(:),u);
% remove non connections
counts(u == 0) = [];
u(u == 0) = [];
% remove self connections
u(counts == 1) = [];
counts(counts == 1) = [];
% block size
blocksize = sqrt(counts);
Related
I want to create a matrix which which has:
The value 1 if the row is odd and the column is odd
The value 1 if the row is even and the column is even
The value 0 Otherwise.
I want to get the same results as the code below, but in a one line (command window) expression:
N=8;
A = zeros(N);
for row = 1:1:length(A)
for column = 1:1:length(A)
if(mod(row,2) == 1 && mod(column,2) == 1)
A(row,column*(mod(column,2) == 1)) = 1;
elseif(mod(row,2)== 0 && mod(column,2) == 0 )
A(row,column*(mod(column,2) == 0)) = 1;
end
end
end
disp(A)
This is the expected result:
1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1
A simple approach is to use implicit expansion of addition, noting that
odd+odd = even+even = 0
So this is your answer:
A = 1 - mod( (1:N) + (1:N).', 2 );
You could also do this with toeplitz, as shown in this MATLAB Answers Post
For a square matrix with number of rows = number of columns = N
A = toeplitz(mod(1:N,2));
If the number of rows (M) is not equal to the number of columns (N) then
A = toeplitz(mod(1:M,2),mod(1:N,2))
FWIW, you're asking a specific case of this question:
How to generate a customized checker board matrix as fast as possible?
Can you take three lines?
N=8;
A = zeros(N);
A(1:2:end, 1:2:end) = 1;
A(2:2:end, 2:2:end) = 1;
One line solution (when N is even):
A = repmat([1, 0; 0 1], [N/2, N/2]);
You can try the function meshgrid to generate mesh grids and use mod to determine even or odd
[x,y] = meshgrid(1:N,1:N);
A = mod(x+y+1,2);
such that
>> A
A =
1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1
I am working on graph theory using adjacency matrix, I want to split the edges between multiple nodes for instance I have the following initial adjacency matrix :
a= [ 0 2 3;
2 0 1;
3 1 0]
From that matrix its clear that we have 3 nodes, Now I want to split the aforementioned rows (edges) into new random nodes between (1-3) :
split= randi([1 3],1,length(A));
split = [ 2 2 1]
I know now that I need to split the elements of the first row into two rows, the elements of the second rows also into two rows, while the elements of th third row will remain as is, and I'll have new matrix with size 5X5 as following:
A = [0 0 2 0 3;
0 0 0 0 0;
2 0 0 0 1;
0 0 0 0 0;
3 0 1 0 0]
What I want to do is to split the non-zero elements in the first row between this row and the second row, and the third with the fourth, so my matrix will look like:
An = [0 0 2 0 0;
0 0 0 0 3;
2 0 0 0 0;
0 0 0 0 1;
0 3 0 1 0]
It's not fully clear to me what's the initial point, the prerequisites and conditions. I assume that every second row/column is a row/column of zeros. I furthermore assume that the non-zero rows/columns have exactly two non-zero values whereas the second value should be moved to the next row/column. For this, I'd suggest:
A = [0 0 2 0 3 ; 0 0 0 0 0 ; 2 0 0 0 1 ; 0 0 0 0 0 ; 3 0 1 0 0];
for n = 1:2
if n==2
A = A';
end % if
for k = 1:2:size(A,1)-1
m = find(A(k,:));
A(k+(0:1),m(end)) = flipud(A(k+(0:1),m(end)));
end % for
if n==2
A = A';
end % if
end % for
A
A =
0 0 2 0 0
0 0 0 0 3
2 0 0 0 0
0 0 0 0 1
0 3 0 1 0
Here An is directly generated from a without creating A:
a = [ 0 2 3;
2 0 1;
3 1 0];
split = [ 2 2 1];
L = length(a);
cum = cumsum([1 split(1:end-1)]);
%ro = rot90(split - (0:L-1).' + cum-1, -1); %MATLAB R2016b
ro = rot90(bsxfun(#minus,split + cum-1 , (0:L-1).') , -1);
co = repmat(cum, L, 1);
idx = triu(true(L), 1);
N = sum(split);
An = zeros(N);
sub = sub2ind([N,N], ro(idx), co(idx));
An(sub) = a(idx);
An = An + An.'
An =
0 0 2 0 0
0 0 0 0 3
2 0 0 0 0
0 0 0 0 1
0 3 0 1 0
I know the code below :
N = 5;
assert(N>1 && mod(N,2)==1);
A = zeros(N);
% diamond mask
N2 = fix(N/2);
[I,J] = meshgrid(-N2:N2);
mask = (abs(I) + abs(J)) == N2;
% fill with zeros
A(mask) = 1;
which transforms matrix A to this:
A=
0 0 1 0 0
0 1 0 1 0
1 0 0 0 1
0 1 0 1 0
0 0 1 0 0
But I want the diamond to be filled with 1.
What should I do?
Here's a vectorized approach using bsxfun -
Nh = (N+1)/2;
range_vec = [1:Nh Nh-1:-1:1];
out = bsxfun(#plus,range_vec(:),range_vec) > Nh
Sample runs -
1) N = 5 :
out =
0 0 1 0 0
0 1 1 1 0
1 1 1 1 1
0 1 1 1 0
0 0 1 0 0
2) N = 9 :
out =
0 0 0 0 1 0 0 0 0
0 0 0 1 1 1 0 0 0
0 0 1 1 1 1 1 0 0
0 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 0
0 0 1 1 1 1 1 0 0
0 0 0 1 1 1 0 0 0
0 0 0 0 1 0 0 0 0
You can use tril and flip functions:
mat = tril(ones(N), round((N-1)/2)) - tril(ones(N), round((-N-1)/2));
out = mat & flip(mat)
Odd values of N:
% N = 5;
out =
0 0 1 0 0
0 1 1 1 0
1 1 1 1 1
0 1 1 1 0
0 0 1 0 0
Even values of N:
% N = 4;
out =
0 1 1 0
1 1 1 1
1 1 1 1
0 1 1 0
What you need is to return a 1 or a 0 based on the Manhattan distance from each array location to the center of your diamond
N = 5;
assert(N>1 && mod(N,2)==1);
A = false(N);
[m, n] = size(A); %dimensions of A
X = floor([m, n]/2); %floored division gives integer indices of center of array
x = X(1); y = X(2);
radius = m/2; %half the height gives the radius
for a = 1 : m
for b = 1 : n
A(a,b) = abs(a-x)+abs(b-y) <= radius; %test if manhatten distance <= radius
end
end
This naturally will need editing to suit your particular case... In particular, the center of your diamond can realistically be placed anywhere by modifying x, y, and the radius can be either smaller or larger than half the width of the array if you so choose.
Just add a for loop and fill all diagonals:
N = 5;
assert(N>1 && mod(N,2)==1);
A = zeros(N);
% diamond mask
N2 = fix(N/2);
[I,J] = meshgrid(-N2:N2);
for id = 0:N2
A((abs(I) + abs(J)) == id) = 1;
end
I have the following two matrices which are outputs of a procedure. The size of the matrices may change but both matrices will always be the same size: size(TwoHopMat_1) == size(Final_matrix)
Example:
TwoHopMat_1 =
0 0 0 0 1
0 0 1 1 0
0 1 0 1 0
0 1 1 0 0
1 0 0 0 0
Final_matrix =
1 0 0 0 1
1 0 0 0 1
1 0 0 0 1
1 1 0 0 0
1 0 0 0 1
Now I need to shuffle the final_matrix such that i meet the following conditions after shuffling:
Every column should have a minimum of one 1s
If i have a 1 in a particular position of TwoHopMat_1 then that particular position should not have 1 after shuffling.
The conditions should work even if we give matrices of size 100x100.
first step: set one element of each column of the result matrix ,that is not 1 in Final_matrix ,to 1
second step: then remaining ones randomly inserted into positions of the result matrix that are not 1 in Final_matrix and are not 1 in the first step result
TwoHopMat_1=[...
0 0 0 0 1
0 0 1 1 0
0 1 0 1 0
0 1 1 0 0
1 0 0 0 0];
Final_matrix=[...
1 0 0 0 1
1 0 0 0 1
1 0 0 0 1
1 1 0 0 0
1 0 0 0 1];
[row col] = size(Final_matrix);
result = zeros(row ,col);
%condition 1 & 2 :
notTwoHop = ~TwoHopMat_1;
s= sum(notTwoHop,1);
c= [0 cumsum(s(1:end - 1))];
f= find(notTwoHop);
r = floor(rand(1, col) .* s) + 1;
i = c + r;
result(f(i)) = 1;
%insert remaining ones randomly into the result
f= find(~(result | TwoHopMat_1));
i = randperm(numel(f), sum(Final_matrix(:))-col);
result(f(i)) =1
A possible solution:
function [result_matrix] = shuffle_matrix(TwoHopMat_1, Final_matrix)
% Condition number 2
ones_mat = ones(size(TwoHopMat_1));
temp_mat = abs(TwoHopMat_1 - ones_mat);
% Condition number 1
ones_to_remove = abs(sum(sum(temp_mat)) - sum(sum(Final_matrix)));
while ones_to_remove > 0
% Random matrix entry
i = floor((size(Final_matrix, 1) * rand())) + 1;
j = floor((size(Final_matrix, 2) * rand())) + 1;
if temp_mat(i,j) == 1
temp_mat(i,j) = 0;
ones_to_remove = ones_to_remove - 1;
end
end
result_matrix = temp_mat;
end
Note: this solution uses brute force.
I have the following code for generating an adjacency matrix for a network.
How do I go about creating a distance matrix (Probably a two hop matrix)from this output.
function adj = AdjMatrixLattice4( N, M )
% Size of adjacency matrix
MN = M*N;
adj = zeros(MN,MN);
for i=1:N
for j=1:N
A = M*(i-1)+j; %Node # for (i,j) node
if(j<N)
B = M*(i-1)+j+1; %Node # for node to the right
C = M*(i-1)+j+2;
D = M*(i-1)+j+2;
adj(A,B) = 1;
adj(B,A) = 1;
adj(A,C) = 1;
adj(C,A) = 1;
adj(A,D) = 1;
adj(D,A) = 1;
end
if(i<M)
B = M*i+j;
C = M*i+j+1; %Node # for node below
D = M*i+j;
adj(A,B) = 1;
adj(B,A) = 1;
adj(A,C) = 1;
adj(C,A) = 1;
adj(A,D) = 1;
adj(D,A) = 1;
end
end
end
end
The output for the following program is
AdjMatrixLattice4(3,3)
ans =
0 1 1 1 1 0 0 0 0 0
1 0 1 1 1 1 0 0 0 0
1 1 0 0 0 1 1 0 0 0
1 1 0 0 1 1 1 1 0 0
1 1 0 1 0 1 1 1 1 0
0 1 1 1 1 0 0 0 1 1
0 0 1 1 1 0 0 1 1 0
0 0 0 1 1 0 1 0 1 1
0 0 0 0 1 1 1 1 0 0
0 0 0 0 0 1 0 1 0 0