How to set up a matrix with a fixed pattern - matlab

I need to write a script to automatically setup a matrix A. The size of this matrix is linked to the value of another variable in the workspace, N. In general, A will have N + N*(N-1)/2 rows and N columns.
The first N rows and N columns are basically just a diagonal matrix, which is easy to setup using diag.
I'm having problems setting up the lower part of the matrix. Basically, it needs to have the following form:
-1 0 0 0
0 -1 0 0
0 0 -1 0
0 0 0 -1
1 -1 0 0
1 0 -1 0
1 0 0 -1
0 1 -1 0
0 1 0 -1
0 0 1 -1
I'm sure the pattern is clear.
How do I code this so that Matlab sets up this matrix for ANY value of N?
Thanks

With some algebraic manipulation:
L=(N*(N+1)/2):-1:1;
R=ceil((sqrt(8*L+1)-1)/2);
A=bsxfun(#eq, N-1:-1:0, R')-bsxfun(#eq, N:-1:1, (L-(R.*(R-1))/2).');

UPDATE
Performance version including preallocation.
N=4;
result = zeros(N*(N+1)/2,N+1);
t = N;
endpos = 0;
for t = N:-1:1
result(endpos+1:endpos+t,:) = [zeros(t, N-t) ones(t,1) -eye(t)];
endpos = endpos + t;
end
result = result(:,2:end);
Note that I have replaced the while loop as you seem to prefer a for.
I will leave the original here as well for comparison:
Here you go:
result = [];
N = 4;
t = MaxN;
while t > 0
block = [zeros(t, N-t) ones(t,1) -eye(t)];
result = [result; block];
t = t-1;
end
result = result(:,2:end);

Thank everyone! I will post my own solution here (doesn't pre-allocate though). Might adjust it to #Dennis's solution.
N = max(size(a));
P = N*(N-1)/2;
A = zeros(N+P,N);
A(1:N,1:N) = diag(-a);
B=[];
for i = N-1:-1:1
Block = [zeros(i,N-1-i) ones(i,1) -eye(i)];
B = [B; Block];
clear Block
end
A(N+1:end,:) = B;
clear N P B i

Related

All possible 0 and 1 matrices of a normal graph

To better explain what I need, here is my first code:
function allpos = f1(x)
allpos=reshape(permute((dec2base(0:power(2,x*x)-1,2)-'0'),[3 2 1]),x,x,[]);
This code does exactly what I need it to. If the users inputs f1(2), it returns every matrix from [0 0; 0 0] to [1 1; 1 1]. However, it also gives me a lot of useless matrices. I only want matrices that are mirrored across the diagonal, with only zeros on the diagonal.
To put more simply, for f1(3), I only want
[0 0 0; 0 0 0; 0 0 0] to [0 1 1; 1 0 1; 1 1 0]. That means that if I run the new f1(3); it will return 8 matrices, not 512.
How do I rewrite the function to do this? I know that it will require some sort of addition of a triangular matrix and its transposed self, but I cant piece it together. Thanks!
It can probably be made more concise but the following does what you ask.
function allpos = f1(x)
N = (x-1)*x / 2;
z = permute(dec2base(0:power(2,N)-1,2)-'0',[3 2 1]);
allpos = zeros(x,x,power(2,N));
idx = repmat(logical(tril(ones(x),-1)),[1,1,power(2,N)]);
allpos(idx) = z(:);
allpos = permute(allpos,[2 1 3]);
allpos(idx) = z(:);
A similar solution to #jodag's one, but more concise...
function allops=f1(x)
allops=zeros(x,x,2^(sum(1:x-1)));
allops(find(triu(ones(x),1))+x^2*(0:2^(sum(1:x-1))-1))=[dec2base(0:2^(sum(1:x-1))-1,2)-'0'].';
allops=allops+permute(allops,[2 1 3])
end

unexpected output from a for loop in matlab

am trying to execute the following code on matlab i've a for loop inside a function and it's supposed to store a result at every iteration ,, but when it executes it just gives me the result of the last iteration,, is there something wrong with the sequence of the code ?
the inpit data1 is a data vector of size (33167*1)
i doubt that there is sometnig wrong with the foor loop.. there is the code i wrote :
function [vectorDominant] = processData(data1 , samplingRate , startRange, endRange)
%(length(data)/1000)
data = data1;
windowSize = 10;
samplingRate = samplingRate;
frame_len = samplingRate*windowSize;
l = length(data);
num_frames = floor(l/frame_len);
t = 1;
for i = 1 : num_frames
frame = data( (i-1)*frame_len+1 : frame_len*i);
% --- haming window ------
hammingData=frame.*hamming(length(frame));
%---- remove dc offset from data ----
dataWindowed = detrend(hammingData);
% ---- apply fft ----
fourierTransform_data=fft(dataWindowed);
y = fourierTransform_data ;
%plot(abs(fourierTransform_data))
%------- find dominant-----
len = (length(frame)/2) -1;
y = y(1:len);
[v,k] = max(y);
fftLength = length(frame);
freq = (0:(samplingRate/fftLength) :(samplingRate/2));
%plot(freq,abs(y)), grid('on'), title('dominant freq')
freq = freq(find(freq>=startRange & freq<=endRange));
dominantFrequency = freq(k);
%array[(length(data)/1000)];
%var = 1;
%while var<= length(num_frames)
n = (length(data)/1000);
vectorDominant = zeros(n,1); % to preallocate it first as it changes size every iterartion
vectorDominant(i) = dominantFrequency;
%var = var+1;
%end
end
end
the argument i give it is processData(ax, 100, 0.1, 2) where ax is the
data vector and it returns the following ans =
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
0
0
0.2000
the whole result must have numbers like the last one (0.2000) not zeros
Every time through the loop you recreate vectorDominant to be a vector of zeros and then set one element of it to be the newly calculated value. Presumably the first 2 of the following lines should be done once before entering the loop.
n = (length(data)/1000);
vectorDominant = zeros(n,1);
vectorDominant(i) = dominantFrequency;
Presumably n should also be num_frames not the value calculated above.

Constructing vectors of different lengths

I want to find out row and column number of zeros in 3 dimensional space. Problem is I get output vectors(e.g row) of different length each time, hence dimension error occurs.
My attempt:
a (:,:,1)= [1 2 0; 2 0 1; 0 0 2]
a (:,:,2) = [0 2 8; 2 1 0; 0 0 0]
for i = 1 : 2
[row(:,i) colum(:,i)] = find(a(:,:,i)==0);
end
You can use linear indexing:
a (:,:,1) = [1 2 0; 2 0 1; 0 0 2];
a (:,:,2) = [0 2 8; 2 1 0; 0 0 0];
% Answer in linear indexing
idx = find(a == 0);
% Transforms linear indexing in rows-columns-3rd dimension
[rows , cols , third] = ind2sub(size(a) ,idx)
More on the topic can be found in Matlab's help
Lets assume your Matrix has the format N-by-M-by-P.
In your case
N = 3;
M = 3;
P = 2;
This would mean that the maximum length of rows and coloms from your search (if all entries are zero) is N*M=9
So one possible solution would be
%alloc output
row=zeros(size(a,1)*size(a,2),size(a,3));
colum=row;
%loop over third dimension
n=size(a,3);
for i = 1 : n
[row_t colum_t] = find(a(:,:,i)==0);
%copy your current result depending on it's length
row(1:length(row_t),i)=row_t;
colum(1:length(colum_t),i)=colum_t;
end
However, when you past the result to the next function / script you have to keep in mind to operate on the non-zero elements.
I would go for the vectorized solution of Zep. As for bigger matrices a it is more memory efficient and I am sure it must be way faster.

How to convert a vector into a matrix where values on columns are 1 where the column number is the vector element, else 0?

I'm unsure how to phrase the question, but I think an example will help. Suppose I have a vector y = [3;1;4;1;6]. I want to create the matrix Y =
[0 0 1 0 0 0;
1 0 0 0 0 0;
0 0 0 1 0 0;
1 0 0 0 0 0;
0 0 0 0 0 1]
↑ ↑ ↑ ↑ ↑ ↑
1 2 3 4 5 6
where the element on each column is one or zero corresponding to the value in the vector.
I found that I could do it using
Y = []; for k = 1:max(y); Y = [Y (y==k)]; end
Can I do it without a for loop (and is this method more efficient if y has thousands of elements)?
Thanks!
Your method is not efficient because you're growing the size of Y in the loop which is not a good programming practice. Here is how your code can be fixed:
Ele = numel(y);
Y= zeros(Ele, max(y));
for k = 1:Ele
Y (k,y(k))= 1;
end
And here is an alternative approach without a loop:
Ele = numel(y); %Finding no. of elements in y
Y= zeros(Ele, max(y)); % Initiailizing the matrix of the required size with all zeros
lin_idx = sub2ind(size(Y), 1:Ele, y.'); % Finding linear indexes
Y(lin_idx)=1 % Storing 1 in those indexes
You can use bsxfun:
result = double(bsxfun(#eq, y(:), 1:max(y)));
If you are running the code on Matlab version R2016b or later, you can simplify the syntax to
result = double(y(:)==(1:max(y)));
Another approach, possibly more efficient, is to fill in the values directly using accumarray:
result = accumarray([(1:numel(y)).' y(:)], 1);
I found another solution:
E = eye(max(y));
Y = E(y,:);
Another solution:
Y = repmat(1:max(y), size(y)) == repmat(y, 1, max(y))

Implementing IMFILTER in matlab

I am trying to filter an image with out using imfilter. I should get the same results as imfilter but I keep getting diffrent results. Can someone tell me where I went wrong?
orignal=imread('obj6__17.png');
filter=1/9*[-1 -1 -1 ; -1 17 -1 ; -1 -1 -1];
s=size(orignal);
r=zeros(s(1));
temp = zeros(3);
for i= 2: s(1)-1
for j = 2: s(2)-1
for n= 1: 3
for m= 1:3
temp(n,m)=orignal(i+2-n,j+2-m)*filter(n,m);
end
end
r(i,j)=sum(single(sum(temp)));
end
end
The size of r should be the same as the original I think. And I don't understand why you convert to single precision using single. Anyway, I think you want to do the following:
%# Let's first create a small test image from the built-in peppers image
original = im2double(imread('peppers.png'));
original = original(1:5,1:8,1);
filter = 1/9 * [-1 -1 -1 ; -1 17 -1 ; -1 -1 -1];
s = size(original);
r = zeros(s);
for i = 2:s(1)-1
for j = 2:s(2)-1
temp = original(i-1:i+1,j-1:j+1) .* filter;
r(i,j) = sum(temp(:));
end
end
The result is as follows:
r =
0 0 0 0 0 0 0 0
0 0.2336 0.2157 0.2514 0.2436 0.2257 0.2344 0
0 0.2453 0.2444 0.2671 0.2693 0.2418 0.2240 0
0 0.2741 0.2728 0.2397 0.2505 0.2375 0.2436 0
0 0 0 0 0 0 0 0
And with imfilter, it is:
r2 = imfilter(original, filter)
r2 =
0.3778 0.3325 0.3307 0.3442 0.3516 0.3312 0.3163 0.3856
0.3298 0.2336 0.2157 0.2514 0.2436 0.2257 0.2344 0.3386
0.3434 0.2453 0.2444 0.2671 0.2693 0.2418 0.2240 0.3512
0.3272 0.2741 0.2728 0.2397 0.2505 0.2375 0.2436 0.3643
0.3830 0.3181 0.3329 0.3403 0.3508 0.3272 0.3412 0.4035
As you see, the results are the same except the ones on the borders. There are a few strategies to compute the ones on the borders as mirroring the image to the out of the borders, keeping them the same, etc. Please read the documentation of imfilter and choose one strategy.
Note that I didn't flipped filter here since the filter is symmetric in both directions. And I recommend you to avoid loops! There are nested loops of depth four in your code!
Lastly, you can use 2-D convolution to do the same as imfilter:
r3 = conv2(original, filter, 'same');
r3 =
0.3778 0.3325 0.3307 0.3442 0.3516 0.3312 0.3163 0.3856
0.3298 0.2336 0.2157 0.2514 0.2436 0.2257 0.2344 0.3386
0.3434 0.2453 0.2444 0.2671 0.2693 0.2418 0.2240 0.3512
0.3272 0.2741 0.2728 0.2397 0.2505 0.2375 0.2436 0.3643
0.3830 0.3181 0.3329 0.3403 0.3508 0.3272 0.3412 0.4035
This is modifies code and gives the exact same result as imfilter....
%# Let's first create a small test image from the built-in peppers image
original = im2double(imread('peppers.png'));
original = original(1:5,1:8,1);
filter = 1/9 * [-1 -1 -1 ; -1 17 -1 ; -1 -1 -1];
s = size(original);
r = zeros(s);
original=padarray(original,[1,1]);
for i = 2:s(1)
for j = 2:s(2)
temp = original(i-1:i+1,j-1:j+1) .* filter;
r(i-1,j-1) = sum(temp(:));
end
end
This gives the result matrix which is exactly same as with the
function...