N行M列のサイズのグリッド上に配置された一連の点を考えます。隣接するポイントが接続されるように隣接行列を作成しようとしています。
たとえば、グラフのある3x3グリッドでは次のようになります。
1-2-3
| | |
4-5-6
| | |
7-8-9
対応する隣接行列が必要です。
+---+------------------------------------------------------+
| | 1 2 3 4 5 6 7 8 9 |
+---+------------------------------------------------------+
| 1 | 0 1 0 1 0 0 0 0 0 |
| 2 | 1 0 1 0 1 0 0 0 0 |
| 3 | 0 1 0 0 0 1 0 0 0 |
| 4 | 1 0 0 0 1 0 1 0 0 |
| 5 | 0 1 0 1 0 1 0 1 0 |
| 6 | 0 0 1 0 1 0 0 0 1 |
| 7 | 0 0 0 1 0 0 0 1 0 |
| 8 | 0 0 0 0 1 0 1 0 1 |
| 9 | 0 0 0 0 0 1 0 1 0 |
+---+------------------------------------------------------+
おまけとして、ソリューションは4と8の両方に接続された隣接ポイントに対して機能する必要があります。
o o o o
o X o vs. o X o
o o o o
これは私がこれまでに持っているコードです:
N = 3; M = 3;
adj = zeros(N*M);
for i=1:N
for j=1:M
k = sub2ind([N M],i,j);
if i>1
ii=i-1; jj=j;
adj(k,sub2ind([N M],ii,jj)) = 1;
end
if i<N
ii=i+1; jj=j;
adj(k,sub2ind([N M],ii,jj)) = 1;
end
if j>1
ii=i; jj=j-1;
adj(k,sub2ind([N M],ii,jj)) = 1;
end
if j<M
ii=i; jj=j+1;
adj(k,sub2ind([N M],ii,jj)) = 1;
end
end
end
すべてのループを回避するためにこれをどのように改善できますか?
気づいた場合は、作成する隣接行列に明確なパターンがあります。具体的には、それらは対称的であり、 バンド です。この事実を利用して、 diag
関数(または spdiags
関数を使用して、疎行列)。上記のサンプルマトリックスを例として使用して、各ケースの隣接マトリックスを作成する方法を次に示します。
mat = [1 2 3; 4 5 6; 7 8 9]; % Sample matrix
[r, c] = size(mat); % Get the matrix size
diagVec1 = repmat([ones(c-1, 1); 0], r, 1); % Make the first diagonal vector
% (for horizontal connections)
diagVec1 = diagVec1(1:end-1); % Remove the last value
diagVec2 = ones(c*(r-1), 1); % Make the second diagonal vector
% (for vertical connections)
adj = diag(diagVec1, 1)+diag(diagVec2, c); % Add the diagonals to a zero matrix
adj = adj+adj.'; % Add the matrix to a transposed copy of
% itself to make it symmetric
そして、次のマトリックスが得られます。
adj =
0 1 0 1 0 0 0 0 0
1 0 1 0 1 0 0 0 0
0 1 0 0 0 1 0 0 0
1 0 0 0 1 0 1 0 0
0 1 0 1 0 1 0 1 0
0 0 1 0 1 0 0 0 1
0 0 0 1 0 0 0 1 0
0 0 0 0 1 0 1 0 1
0 0 0 0 0 1 0 1 0
mat = [1 2 3; 4 5 6; 7 8 9]; % Sample matrix
[r, c] = size(mat); % Get the matrix size
diagVec1 = repmat([ones(c-1, 1); 0], r, 1); % Make the first diagonal vector
% (for horizontal connections)
diagVec1 = diagVec1(1:end-1); % Remove the last value
diagVec2 = [0; diagVec1(1:(c*(r-1)))]; % Make the second diagonal vector
% (for anti-diagonal connections)
diagVec3 = ones(c*(r-1), 1); % Make the third diagonal vector
% (for vertical connections)
diagVec4 = diagVec2(2:end-1); % Make the fourth diagonal vector
% (for diagonal connections)
adj = diag(diagVec1, 1)+... % Add the diagonals to a zero matrix
diag(diagVec2, c-1)+...
diag(diagVec3, c)+...
diag(diagVec4, c+1);
adj = adj+adj.'; % Add the matrix to a transposed copy of
% itself to make it symmetric
そして、次のマトリックスが得られます。
adj =
0 1 0 1 1 0 0 0 0
1 0 1 1 1 1 0 0 0
0 1 0 0 1 1 0 0 0
1 1 0 0 1 0 1 1 0
1 1 1 1 0 1 1 1 1
0 1 1 0 1 0 0 1 1
0 0 0 1 1 0 0 1 0
0 0 0 1 1 1 1 0 1
0 0 0 0 1 1 0 1 0
楽しみのために、グリッド上のすべてのポイントのペア間の距離を計算することにより隣接行列を構築するソリューションを次に示します(明らかに最も効率的な方法ではありません)。
N = 3; M = 3; %# grid size
CONNECTED = 8; %# 4-/8- connected points
%# which distance function
if CONNECTED == 4, distFunc = 'cityblock';
elseif CONNECTED == 8, distFunc = 'chebychev'; end
%# compute adjacency matrix
[X Y] = meshgrid(1:N,1:M);
X = X(:); Y = Y(:);
adj = squareform( pdist([X Y], distFunc) == 1 );
そして、隣接行列と接続されたポイントのグラフを視覚化するためのいくつかのコードがあります:
%# plot adjacency matrix
subplot(121), spy(adj)
%# plot connected points on grid
[xx yy] = gplot(adj, [X Y]);
subplot(122), plot(xx, yy, 'ks-', 'MarkerFaceColor','r')
axis([0 N+1 0 M+1])
%# add labels
[X Y] = meshgrid(1:N,1:M);
X = reshape(X',[],1) + 0.1; Y = reshape(Y',[],1) + 0.1;
text(X, Y(end:-1:1), cellstr(num2str((1:N*M)')) )
同じ問題を検索しているときにこの質問を見つけました。ただし、スパースマトリックスタイプを使用する必要がある問題サイズのため、提供されたソリューションはどれも私にとってはうまくいきませんでした。大規模なインスタンスで機能する私のソリューションは次のとおりです。
function W = getAdjacencyMatrix(I)
[m, n] = size(I);
I_size = m*n;
% 1-off diagonal elements
V = repmat([ones(m-1,1); 0],n, 1);
V = V(1:end-1); % remove last zero
% n-off diagonal elements
U = ones(m*(n-1), 1);
% get the upper triangular part of the matrix
W = sparse(1:(I_size-1), 2:I_size, V, I_size, I_size)...
+ sparse(1:(I_size-m),(m+1):I_size, U, I_size, I_size);
% finally make W symmetric
W = W + W';
ちょうどこの質問に出くわしました。私は素敵なm関数を持っています(リンク: sparse_adj_matrix.m
)非常に一般的です。
4接続グリッド(L1ノルムによる半径1)、8接続グリッド(L_inftyノルムによる半径1)を処理できます。
3Dをサポートすることもできます(任意に上位のグリッド)。
この関数は、radius = 1以外のノードも接続できます。
関数のシグニチャーは次のとおりです。
% Construct sparse adjacency matrix (provides ii and jj indices into the
% matrix)
%
% Usage:
% [ii jj] = sparse_adj_matrix(sz, r, p)
%
% inputs:
% sz - grid size (determine the number of variables n=prod(sz), and the
% geometry/dimensionality)
% r - the radius around each point for which edges are formed
% p - in what p-norm to measure the r-ball, can be 1,2 or 'inf'
%
% outputs
% ii, jj - linear indices into adjacency matrix (for each pair (m,n)
% there is also the pair (n,m))
%
% How to construct the adjacency matrix?
% >> A = sparse(ii, jj, ones(1,numel(ii)), prod(sz), prod(sz));
%
%
% Example:
% >> [ii jj] = sparse_adj_matrix([10 20], 1, inf);
% construct indices for 200x200 adjacency matrix for 8-connect graph over a
% grid of 10x20 nodes.
% To visualize the graph:
% >> [r c]=ndgrid(1:10,1:20);
% >> A = sparse(ii, jj, 1, 200, 200);;
% >> gplot(A, [r(:) c(:)]);
現在のコードはそれほど悪くないようです。何らかの方法で、すべての隣接ペアを反復処理する必要があります。コードを最適化する必要がある場合は、次のことをお勧めします。
1 <= i <= (N*M)
[i-M, i+1, i+M, i-1]
です。ノードのすべての隣接ペアを取得することに注意してください。
i % M != 0
の「右」の近傍(つまり、水平エッジ)を計算するだけです(Matlabは0ベースではなく1ベースであるため)i > M
の "上"の近傍(つまり、垂直エッジ)のみを計算する必要がありますこれは単一のループ(ただし、N * Mの反復数は同じ)に依存し、sub2ind()を呼び出さず、ループ内のifステートメントは2つだけです。
グラフのノードごとに、右と1つ下に接続を追加します。グリッドに到達しすぎていないことを確認してください。隣接行列を作成する次の関数を考えます。
_function adj = AdjMatrixLattice4( N, M )
% Size of adjacency matrix
MN = M*N;
adj = zeros(MN,MN);
% number nodes as such
% [1]---[2]-- .. --[M]
% | | |
% [M+1]-[M+2]- .. -[2*M]
% : : :
% [] [] .. [M*N]
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
adj(A,B) = 1;
adj(B,A) = 1;
end
if(i<M)
B = M*i+j; %Node # for node below
adj(A,B) = 1;
adj(B,A) = 1;
end
end
end
end
_
上記の例AdjMatrixLattice4(3,3)=
_ 0 1 0 1 0 0 0 0 0
1 0 1 0 1 0 0 0 0
0 1 0 0 0 1 0 0 0
1 0 0 0 1 0 1 0 0
0 1 0 1 0 1 0 1 0
0 0 1 0 1 0 0 0 1
0 0 0 1 0 0 0 1 0
0 0 0 0 1 0 1 0 1
0 0 0 0 0 1 0 1 0
_