1
votes

The title might be confusing, here's a particular example to explain myself. Also, I'm not sure how do you call the diagonal that starts in (1,2) and goes onward: (2,3) ; (3,4) and so on. Non-principal, non-main diagonal, not sure at all.

3x3 case
-1  1 0
-1  0 1
 0 -1 1

4x4 case
-1  1 0 0
-1  0 1 0
-1  0 0 1
 0 -1 1 0
 0 -1 0 1
 0 0 -1 1

So if the original matrix was a 4x4 (or any other size), I am able to make a matrix the size of the second example. I now have to insert the -1 and 1's in this fashion. This means n-1 number of -1's inserted if j=1, and then, a n-1 number of ones in the non-principal diagonal. When this is done, it's the same but for j=2 and the next non-principal diagonal, and so on.

Thing is, I'm thinking all the time about loops, and too many cases arise, because what I want is to be able to do this for any possible dimension, not for a particular case.

But then I saw this post Obtaining opposite diagonal of a matrix in Matlab

With this answer: A(s:s-1:end-1)

And it seems like a much cleaner way of doing it, since my own way (not finished since I'm not able to figure all the cases) has too many conditions. With a sentence like that, I could choose the diagonal, insert ones, and do it as many times as required, depending of the n dimension.

This leaves the problem of inserting the -1's, but I guess I could manage something.

3
I don't really see what the input and desired output are. Can you clarify?Luis Mendo
Uhmm I tried that with the 4x4 case. As in, this is part of the code required to solve logarithmic least squares. With that in mind (didn't know if adding that to the post would clarify anything) The input is the dimensions of the matrix. So if it's a 4x4 case, the output is what I wrote. If it's a 3x3, the output is much clean and it's the 3x3 example I wrote. If it's a 5x5 matrix what I need, the output would be the same, but with 1 more column and the diagonals would reach further. Not sure if I am explaining myself properlykeont
I see. So both examples are outputs. Have you checked diag (with two arguments)?Luis Mendo
I did, that and polyfit. Can't say I quite get them. Closer I get is: v = diag(X,k), but this gives me a diagonal, doesn't allow me to write on it, as far as I get it.keont
@keont Added Approach #2 in my solution, if you need to explain it to someone.Divakar

3 Answers

2
votes

It seems to mee that you want to obtain the following matrix B of size n × (n-1)*n/2

n = 4;

idx = fliplr(fullfact([n n]));
idx(diff(idx')<=0,:) = [];

m = size(idx,1);

B = zeros(m,n);
B(sub2ind(size(B),1:m,idx(:,1)')) = -1;
B(sub2ind(size(B),1:m,idx(:,2)')) = 1;
1
votes

Approach #1

Here's a vectorized approach that has more memory requirements than a non-vectorized or for-loop based one. So, it could be tried out for small to medium sized datasizes.

The basic idea is this. For n=4 as an example, we take

-1  1 0 0
-1  0 1 0
-1  0 0 1

as the basic building block, replicate it n-1 i.e. 3 times and then remove the rows that aren't supposed to be part of the final output as per the requirements of the problem. Because of this very nature, this solution has more memory requirements, as we need to remove rows 6,8,9 for n = 4 case. But this gives us the opportunity to work with everything in one go.

N = n-1; %// minus 1 of the datasize, n
blksz = N*(N+1); %// number of elements in a (n-1)*n blocksize that is replicated

b1 = [-1*ones(N,1) eye(N)] %// Create that special starting (n-1)*n block

idx1 = find(b1~=0) %// find non zero elements for the starting block
idx2 = bsxfun(@plus,idx1,[0:N-1]*(blksz+N)) %// non zero elements for all blocks

b1nzr = repmat(b1(b1~=0),[1 N]) %// elements for all blocks

vald_ind = bsxfun(@le,idx2,[1:N]*blksz) %// positions of valid elements all blocks

mat1 = zeros(N,blksz) %// create an array for all blocks
mat1(idx2(vald_ind)) = b1nzr(vald_ind)  %// put right elements into right places

%// reshape into a 3D array, join/concatenate along dim3
out = reshape(permute(reshape(mat1,N,N+1,[]),[1 3 2]),N*N,[]) 

%// remove rows that are not entertained according to the requirements of problem
out = out(any(out==1,2),:) 

Approach #2

Here's a loop based code that could be easier to get a hold on if you have to explain it to yourself or just people and most importantly scales up pretty well on performance criteria across varying datasizes.

start_block = [-1*ones(n-1,1) eye(n-1)] %// Create that special starting (n-1)*n block

%// Find starting and ending row indices for each shifted block to be repeated
ends = cumsum([n-1:-1:1]) 
starts = [1 ends(1:end-1)+1]

out = zeros(sum(1:n-1),n) %// setup all zeros array to store output
for k1 = 1:n-1

    %// Put elements from shifted portion of start_block for creating the output
    out(starts(k1):ends(k1),k1:end) = start_block(1:n-k1,1:n-k1+1) 
end

With n=4, the output -

out =
    -1     1     0     0
    -1     0     1     0
    -1     0     0     1
     0    -1     1     0
     0    -1     0     1
     0     0    -1     1
0
votes

I don't know if I understood properly, but is this what you are looking for:

M=rand(5);

k=1; % this is to select the k-th diagonal
D=diag(ones(1,size(M,2)-abs(k)), k);

M(D==1)=-1;

M =

0.9834   -1.0000    0.8402    0.6310    0.0128
0.8963    0.1271   -1.0000    0.3164    0.6054
0.8657    0.6546    0.3788   -1.0000    0.5765
0.8010    0.8640    0.2682    0.4987   -1.0000
0.5550    0.2746    0.1529    0.7386    0.6550