Benny Müller
Benny Müller

Reputation: 175

Matlab decreasing matrix diagonal

I want to create a matrix where the middle diagonal is symmetrically decreasing to the sides, like this:

5 4 3 2 1
4 5 4 3 2
3 4 5 4 3
2 3 4 5 4
1 2 3 4 5

The matrix has to be 100x100 and the values are between 0 and 1. Until now I only get the edges and the middle diagonal, but can't get the idea on how to automatically fill the rest.

v = ones(1,100);
green = diag(v);
green(:,1) = fliplr(0:1/99:1);
green(1,:) = fliplr(0:1/99:1);
green(100,:) = 0:1/99:1;
green(:,100) = 0:1/99:1;

Upvotes: 6

Views: 443

Answers (4)

rayryeng
rayryeng

Reputation: 104474

I'm surprised no one has recommended the toeplitz matrix to you:

n = 5;
out = toeplitz(n:-1:1);

We get:

out = 


     5     4     3     2     1
     4     5     4     3     2
     3     4     5     4     3
     2     3     4     5     4
     1     2     3     4     5

If you want to normalize this to [0,1], simply do standard normalization such that:

out_new = (out - 1) / (n - 1)

... and so:

>> out = (out - 1) / (n - 1)

out =

    1.0000    0.7500    0.5000    0.2500         0
    0.7500    1.0000    0.7500    0.5000    0.2500
    0.5000    0.7500    1.0000    0.7500    0.5000
    0.2500    0.5000    0.7500    1.0000    0.7500
         0    0.2500    0.5000    0.7500    1.0000

Upvotes: 6

Divakar
Divakar

Reputation: 221524

How about some code-golfing -

n = 5
M = mod(bsxfun(@plus,n:-1:1,(0:n-1)'),n)
out = triu(M)+tril(n-M)

For your actual case, since you need to have values in the range [0,1], you can scale out, like so -

out = (out - 1)/max(out(:)-1)

Sample run -

>> n = 5;
M = mod(bsxfun(@plus,n:-1:1,(0:n-1)'),n);
out = triu(M)+tril(n-M);
>> out
out =
     5     4     3     2     1
     4     5     4     3     2
     3     4     5     4     3
     2     3     4     5     4
     1     2     3     4     5
>> out = (out - 1)/max(out(:)-1)
out =
            1         0.75          0.5         0.25            0
         0.75            1         0.75          0.5         0.25
          0.5         0.75            1         0.75          0.5
         0.25          0.5         0.75            1         0.75
            0         0.25          0.5         0.75            1

Upvotes: 5

Dennis Klopfer
Dennis Klopfer

Reputation: 769

To look for a vectorized solution consider using spdiags().

n = 5;
A = repmat([1:n-1,n:-1:1],n,1);
B = full(spdiags(A,-n+1:n-1,n,n));

This will return:

5 4 3 2 1
4 5 4 3 2
3 4 5 4 3
2 3 4 5 4
1 2 3 4 5

As @Adriaan pointed out B = B/n will transform the matrix values between 0 and 1.

Upvotes: 7

Adriaan
Adriaan

Reputation: 18177

N = 100; %// size of your matrix
v = ones(1,N); %// get a vector of ones
D = N*diag(v); %// set the main diagonal
for ii = 1:size(D,1)-1
    tmp = (N-ii)*diag(v(1:end-ii),ii); %//positive direction off-
    tmp2 = (N-ii)*diag(v(1:end-ii),-ii); %//negative direction off-diagonal
    D = D+tmp+tmp2; %// Add them up
end
D = D/N; %// scale values to between 0 and 1

The trick here is to use the indexing variable, ii, as a counter to simultaneously decrease the multiplication, N-ii, decrease the length of v, v(1:end-ii) and increase the offset of the diagonal within diag, ii or -ii.

Just to verify plot the results using imagesc(D):

![enter image description here

Upvotes: 4

Related Questions