Is there a way to do this without a for-loop? - matlab

Currently I have this function in MATLAB
function [ y ] = pyramid( x )
%PYRAMID Returns a "pyramid"-shapped matrix.
y = zeros(x); % Creates an empty matrix of x by x.
rings = ceil(x/2); % Compute number of "rings".
for n = 1:rings
% Take the first and last row of the ring and set values to n.
y([n,x-n+1],n:x-n+1) = n*ones(2,x-2*(n-1));
% Take the first and last column of the ring and set values to n.
y(n:x-n+1,[n,x-n+1]) = n*ones(x-2*(n-1),2);
end
end
Which produces the following output:
piramide(4)
ans =
1 1 1 1
1 2 2 1
1 2 2 1
1 1 1 1
piramide(5)
ans =
1 1 1 1 1
1 2 2 2 1
1 2 3 2 1
1 2 2 2 1
1 1 1 1 1
piramide(6)
ans =
1 1 1 1 1 1
1 2 2 2 2 1
1 2 3 3 2 1
1 2 3 3 2 1
1 2 2 2 2 1
1 1 1 1 1 1
Is there a way to achive the same result without using a for-loop ?

If you have the Image Processing Toolbox you can use bwdist:
function y = pyramid(x)
m([1 x], 1:x) = 1;
m(1:x, [1 x]) = 1;
y = bwdist(m,'chessboard')+1;
end
Other solution using min:
pyramid = #(x) min(min((1:x),(1:x).'), min((x:-1:1),(x:-1:1).'));

Related

Modify parts of a matrix based on linear equation on row and column numbers

For example:
>> tmp = ones(5,5)
tmp =
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
I want a command like:
tmp(colNum - 2*rowNum > 0) = 0
that modifies entries of tmp when the column number is more than twice the row number e.g. it should produce:
tmp =
1 1 0 0 0
1 1 1 1 0
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
As a second example, tmp(colNum - rowNum == 0) = 0 should set the diagonal elements of tmp to be zero.
A possibly more efficient solution is to use bsxfun like so
nRows = 5;
nCols = 5;
bsxfun(#(col,row)~(col - 2*row > 0), 1:nCols, (1:nRows)')
You can generalize this to just accept a function so it becomes
bsxfun(#(col,row)~f(col,row), 1:nCols, (1:nRows)')
And now just replace f with exactly the way you specify the equation in your question i.e.
f = #(colNum, rowNum)(colNum - 2*rowNum > 0)
or
f = #(colNum, rowNum)(colNum - rowNum == 0)
of course it might make more sense to specify your function to accept (row,col) instead of (col,row) as that's how MATLAB indexes
You can use meshgrid to generate a grid of 2D coordinates, then use this to impose any condition you wish. The variant you seek outputs 2 2D matrices where the first matrix gives you the column locations and the second matrix outputs the row locations.
For example, given your situation above:
>> [X,Y] = meshgrid(1:5, 1:5)
X =
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
Y =
1 1 1 1 1
2 2 2 2 2
3 3 3 3 3
4 4 4 4 4
5 5 5 5 5
You can see that each unique spatial location shared between X and Y give you the desired 2D location as if you were envisioning a 2D grid.
Therefore, you would do something like this for your first situation:
[X,Y] = meshgrid(1:5,1:5); % Generate 2D coordinates
tmp = ones(5); % Generate desired matrix
tmp(X > 2*Y) = 0; % Set desired locations to 0
We get:
>> tmp
tmp =
1 1 0 0 0
1 1 1 1 0
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
Finally for your second example:
[X,Y] = meshgrid(1:5,1:5); % Generate 2D coordinates
tmp = ones(5); % Generate desired matrix
tmp(X == Y) = 0; % Set desired locations to 0
We get:
>> tmp
tmp =
0 1 1 1 1
1 0 1 1 1
1 1 0 1 1
1 1 1 0 1
1 1 1 1 0
Simply put, generate a grid of 2D coordinates, then use those directly to index into your desired matrix using logical / Boolean conditions to set the desired locations to 0.

Finding a critical point in matrix

I'm attempting to find a critical point in a matrix. The value at index (i,j) should be greater than or equal to all elements in its row, and less than or equal to all elements in its column.
Here is what I have (it's off but I'm close):
function C = critical(A)
[nrow ncol] = size(A);
C = [];
for i = 1:nrow
for j = 1:ncol
if (A(i,j) >= A(i,1:end)) && (A(i,j) <= A(1:end,j))
C = [C ; A(i,j)]
end
end
end
You can use logical indexing.
minI = min(A,[],1);
maxI = max(A,[],2);
[row,col] = find(((A.'==maxI.').' & A==minI) ==1)
Details
Remember that Matlab is column major. We therefore transpose A and maxI.
A = [
3 4 1 1 2
2 4 2 1 4
4 3 2 1 2
3 3 1 1 1
2 3 0 2 1];
A.'==maxI.'
ans =
0 0 1 1 0
1 1 0 1 1
0 0 0 0 0
0 0 0 0 0
0 1 0 0 0
Then do the minimum
A==minI
ans =
0 0 0 1 0
1 0 0 1 0
0 1 0 1 0
0 1 0 1 1
1 1 1 0 1
And then multiply the two
((A.'==maxI.').' & A==minI)
ans =
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 1 0 0 0
0 1 0 0 0
Then find the rows and cols
[row,col] = find(((A.'==maxI.').' & A==minI) ==1)
row =
4
5
col =
2
2
Try this vectorised solution using bsxfun
function [ r,c,criP ] = critical( A )
%// finding the min and max values of each col & row resptly
minI = min(A,[],1);
maxI = max(A,[],2);
%// matching all the values of min & max for each col and row resptly
%// getting the indexes of the elements satisfying both the conditions
idx = find(bsxfun(#eq,A,maxI) & bsxfun(#eq,A,minI));
%// getting the corresponding values from the indexes
criP = A(idx);
%// Also getting corresponding row and col sub
[r,c] = ind2sub(size(A),idx);
end
Sample Run:
r,c should be a vector of equal length which represents the row and column subs of each Critical point. While val is a vector of same length giving the value of the critical point itself
>> A
A =
3 4 1 1 2
2 4 2 1 4
4 3 2 1 2
3 3 1 1 1
2 3 0 2 1
>> [r,c,val] = critical(A)
r =
4
5
c =
2
2
val =
3
3
I think there is a simpler way with intersect:
>> [~, row, col] = intersect(max(A,[],2), min(A));
row =
4
col =
2
UPDATE:
With intersect, in case you have multiple critical points, it will only give you the first one. To have all the indicies, there is also another simple way:
>> B
B =
3 4 1 4 2 5
2 5 2 4 4 4
4 4 2 4 2 4
3 4 1 4 1 4
2 5 4 4 4 5
>> row = find(ismember(max(B,[],2),min(B)))
row =
3
4
>> col = find(ismember(min(B),max(B,[],2)))
col =
2 4 6
Note that the set of critical points now should be the combination of row and col, means you have total 6 critical points in this example: (3,2),(4,2),(3,4),(4,4),(3,6),(4,6).
Here you can find how to export such combination.

Iterate one vector through another in Matlab Part 2

This is a similar request to my post at Iterate one vector through another in Matlab
I am using Luis' suggestion with the following code:
E=[1 2 3 4 5 6 7 8 9 10];
A = [1 2];
s = size(E,2);
t = numel(A);
C = cell(1,s);
[C{:}] = ndgrid(A);
C = cat(s+1, C{:});
C = fliplr(reshape(C, t^s, s));
This produces a good result for C as a 1024x10 matrix with all possible permutations of 1 and 2 to a length of 10 columns. What I want to do is remove any rows that are not in increasing order. For example now I get:
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 2
1 1 1 1 1 1 1 1 2 1
1 1 1 1 1 1 1 1 2 2
All are valid except for the third row since it goes from 2 to back to 1.
I have code to get the desired result, but it is very slow and inefficient.
E=[1 2 3 4 5 6 7 8 9 10];
A = [1 2];
s = size(E,2);
t = numel(A);
C = cell(1,s);
[C{:}] = ndgrid(A);
C = cat(s+1, C{:});
C = fliplr(reshape(C, t^s, s));
min=0;
for row=1:size(C,1)
for col=1:size(C,2)
if(C(row,col)>min)
min=C(row,col);
elseif(C(row,col)<min)
C(row,:)=0;
continue;
end
end
min=0;
end
C = C(any(C,2),:); %remove all zero rows
The desired output is now:
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 2
1 1 1 1 1 1 1 1 2 2
1 1 1 1 1 1 1 2 2 2
1 1 1 1 1 1 2 2 2 2
1 1 1 1 1 2 2 2 2 2
1 1 1 1 2 2 2 2 2 2
1 1 1 2 2 2 2 2 2 2
1 1 2 2 2 2 2 2 2 2
1 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2
Any ideas on how to optimize my code so I do not need to use nested loops?
The super-simple but not-quite-so-obvious solution via a couple of row-wise operations:
d = diff(C, [], 2);
m = min(d, [], 2);
C = C(m>=0, :);
Of course, in this particular example it would be far easier to just generate the resulting matrix directly:
C = flipud(triu(ones(s+1,s).*(max(A)-min(A))) + min(A));
but I assume you're also interested in less trivial values of A ;)

Matrix creation Octave / Matlab, loopless solution request

I want to create a matrix like
A = [0 0 0 0 1;
0 0 0 1 1;
0 0 0 1 1;
0 0 0 1 1;
0 0 1 1 1;
0 1 1 1 1]
based on a vector indicating how many '0's should precede '1's on each row:
B = [4 3 3 3 2 1]
Is there a loopless way to do this ?
You don't mention in your question how the horizontal size of the array should be defined (the number of ones).
For predefined width you can use this code:
width = 5;
A = cell2mat(arrayfun(#(x) [ zeros(1,x), ones(1,width-x) ], B, 'UniformOutput', false)');
If you want that A has minimal width, but still at least one 1 in every row:
A = cell2mat(arrayfun(#(x) [ zeros(1,x), ones(1,max(B)+1-x) ], B, 'UniformOutput', false)');
A shorter “old-school” way to achieve this without a loop would be as follows:
A = repmat(B',1,max(B)+1)<repmat([1:max(B)+1],size(B,2),1)
If you want to have a minimum number of ones
min_ones=1; %or whatever
A = repmat(B',1,max(B)+min_ones)<repmat([1:max(B)+min_ones],size(B,2),1)
I don’t know how this compares speedwise to #nrz’s approach (I’ve only got Octave to hand right now), but to me it's more intuitive as it’s simply comparing a max(B) + min_ones * column tiling of B:
4 4 4 4 4
3 3 3 3 3
3 3 3 3 3
3 3 3 3 3
2 2 2 2 2
1 1 1 1 1
with a row tiling of [1 : max(B) + min_ones]
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
To generate:
A =
0 0 0 0 1
0 0 0 1 1
0 0 0 1 1
0 0 0 1 1
0 0 1 1 1
0 1 1 1 1
This requires only one line, and seems to be faster than previous solutions based on repmat or arrayfun:
%// Example data
ncols = 5;
B = [4 3 3 3 2 1];
%// Generate A
A = bsxfun(#gt, 1:ncols, B(:));

Finding maxima in a vector using MATLAB

i'm trying to find local maxima of a vector of numbers using MATLAB. The built-in findpeaks function will work for a vector such as:
[0 1 2 3 2 1 1 2 3 2 1 0]
where the peaks (each of the 3's) only occupy one position in the vector, but if I have a vector like:
[0 1 2 3 3 2 1 1 2 3 2 1 0]
the first 'peak' occupies two positions in the vector and the findpeaks function won't pick it up.
Is there a nice way to write a maxima-finding function which will detect these sort of peaks?
You can use the REGIONALMAX function from the Image Processing Toolbox:
>> x = [0 1 2 3 3 2 1 1 2 3 2 1 0]
x =
0 1 2 3 3 2 1 1 2 3 2 1 0
>> idx = imregionalmax(x)
idx =
0 0 0 1 1 0 0 0 0 1 0 0 0
Something much easier:
a = [1 2 4 5 5 3 2];
b = find(a == max(a(:)));
output:
b = [4,5]
a = [ 0 1 2 3 3 2 1 2 3 2 1 ];
sizeA = length(a);
result = max(a);
for i=1:sizeA,
if a(i) == result(1)
result(length(result) + 1) = i;
end
end
result contains the max, followed by all the values locations that are equal to max.