Reputation: 209
Let us say that I have 7 values: a, b, c, d, e, f and g
I would like to construct an m by n matrix in this way:
[ a b c d 0 0 0 0 . . . .
[ b e f g 0 0 0 0 . . . .
[ c f a b c d 0 0 . . . .
[ d g b e f g 0 0 . . . .
[ 0 0 c f a b c d 0 0 . .
[ 0 0 d g b e f g 0 0 . .
[ . . 0 0 c f a b c d . .
[ . . 0 0 d g b e f g . .
And so forth...
Therefore, the desired matrix is symmetrical. Values a and e alternate on the main diagonal; values b and f alternate on the 1st upper diagonal; values c and g alternate on the 2nd upper diagonal; values d and 0 alternate on the 3rd upper diagonal. I would like to be able to specify the matrix size with m by n parameters.
I used to do this easily with the SparseArray
and Band
functions in Mathematica, but I cannot find equivalent functions in Matlab. Would there be an efficient way to construct this kind of matrix in Matlab?
Upvotes: 2
Views: 743
Reputation: 766
You can use the spdiags function
Using numbers instead of letters (sparse does not support symbols) like this:
m = 10;
n = 10;
B = repmat([1:4;[5:7 0]],n/2,1);
B=[B(:,end:-1:2) B];
d=-3:3;
A = spdiags(B,d,m,n);
full(A)
which outputs:
1 6 3 0 0 0 0 0 0 0
2 5 2 7 4 0 0 0 0 0
3 6 1 6 3 0 0 0 0 0
4 7 2 5 2 7 4 0 0 0
0 0 3 6 1 6 3 0 0 0
0 0 4 7 2 5 2 7 4 0
0 0 0 0 3 6 1 6 3 0
...
Explanation:
The B matrix contains the values needed on the diagonals in its columns. It is first constructed using B = repmat([1:4;[5:7 0]],n/2,1);
which makes the first two rows repeat n/2 times. At that point it looks like:
1 2 3 4
5 6 7 0
1 2 3 4
5 6 7 0
1 2 3 4
5 6 7 0
1 2 3 4
...
Then it is mirrored to give it the diagonals that are in the lower diagonal part using B=[B(:,end:-1:2) B];
which makes it look like:
4 3 2 1 2 3 4
0 7 6 5 6 7 0
4 3 2 1 2 3 4
0 7 6 5 6 7 0
4 3 2 1 2 3 4
0 7 6 5 6 7 0
4 3 2 1 2 3 4
...
These are the diagonals passed to the spdiags
function.
The end
is a MATLAB keyword which refers to the last column or row. That indexing line says, in words: "Take all the rows of B, and the columns starting from the end, going back 1 step at a time until the second column, and place that submatrix before B." That is how the mirroring is accomplished.
Upvotes: 2
Reputation: 8401
You can use spdiags
to specify the upper diagonals in a sparse matrix and then add the transpose of the strictly upper triangular part for exact symmetry:
>> n = 6;
>> a = 1;b = 2;c = 3;d = 4;e = 5;f = 6;g = 7;
>> n = 6;
>> A = spdiags(repmat([[a;e] , [f;b] , [c;g] , [0;d]],n/2,1),0:3,n,n);
>> A = A + triu(A,1).';
>> issymmetric(A)
ans =
1
>> full(A)
ans =
1 2 3 4 0 0
2 5 6 7 0 0
3 6 1 2 3 4
4 7 2 5 6 7
0 0 3 6 1 2
0 0 4 7 2 5
You might notice I flipped b
/f
and 0
/d
to adjust for the filling behavior; there may be a better way to do this.
For non-square matrices with potentially odd dimension number, I would build the minimum-sized square sparse matrix that has the actual one as a sub-matrix and mask out the unneeded portion at the end:
>> m = 13;
>> n = 6;
>> p = max([m,n]);
>> p = p + mod(p,2); % added to make p even.
>> A = spdiags(repmat([[a;e] , [f;b] , [c;g] , [0;d]],p/2,1),0:3,p,p);
>> A = A + triu(A,1).';
>> A = A(1:m,1:n);
>> full(A)
ans =
1 2 3 4 0 0
2 5 6 7 0 0
3 6 1 2 3 4
4 7 2 5 6 7
0 0 3 6 1 2
0 0 4 7 2 5
0 0 0 0 3 6
0 0 0 0 4 7
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
It's a tad inefficient to build, but easy and straight-forward.
Upvotes: 3