This question already has an answer here:
-
Linspace using matrix input matlab
1 answer
Given an array like a = [ -1; 0; 1];
. For each a(i)
, I need to compute a linearly spaced vector with linspace(min(a(i),0),max(a(i),0),3);
, where each linspace-vector should be stored into a matrix:
A = [-1 -0.5 0;
0 0 0;
0 0.5 1];
With a for loop, I can do this like so:
for i=1:3
A(i) = linspace(min(a(i),0),max(a(i),0),3);
end
How can I achieve this without using loops?
The fastest way I can think of is calculating the step-size, construct the vector from that using implicit binary expansion.
a = [ -1; 0; 1];
n = 3;
stepsizes = (max(a,0)-min(a,0))/(n-1);
A = min(a,0) + (0:(n-1)).*stepsizes;
Timeit:
A couple of timeit
results using (use timeit(@SO)
and remove comments from the blocks to be timed):
function SO()
n = 1e3;
m = 1e5;
a = randi(9,m,1)-4;
% %Wolfie
% aminmax = [min(a, 0), max(a,0)]';
% A = interp1( [0,1], aminmax, linspace(0,1,n) )';
% %Nicky
% stepsizes = (max(a,0)-min(a,0))/(n-1);
% A = min(a,0) + (0:(n-1)).*stepsizes;
% %Loop
% A = zeros(m,n);
% for i=1:m
% A(i,:) = linspace(min(a(i),0),max(a(i),0),n);
% end
%Arrayfun:
A = cell2mat(arrayfun(@(x) linspace(min(x,0),max(x,0),n),a,'UniformOutput',false));
Then the times are:
- Wolfie: 2.2243 s
- Mine: 0.3643 s
- Standard loop: 1.0953 s
arrayfun
: 2.6298 s
Take a = [ -1; 0; 1]
. Create the min / max array:
aminmax = [min(a, 0), max(a,0)].';
Now use interp1
N = 3; % Number of interpolation points.
b = interp1( [0,1], aminmax, linspace(0,1,N) ).';
>> b =
-1.0000 -0.5000 0
0 0 0
0 0.5000 1.0000
One of the possible solutions is to use arrayfun
that applies a function to each element of the array. You also want to convert your results into a matrix, since the output is in the cell array. Since the output of the arrayfun
is non-scalar, you have to turn off uniform output.
cell2mat(arrayfun(@(x) linspace(min(x,0),max(x,0),3),a,'UniformOutput',false))
Edit: I performed some testing using tic-toc method on 100000 long arrays. I found out, that the solution with arrayfun
takes approx. 1.5 time longer than the one you suggested with for loops.
The fastest approach would be to calculate what you need using matrix-vector operation. For example, if you only need to calculate linspace
with 3 elements, you can use something like:
[min(a(:),0), (max(a(:),0)+min(a(:),0))/2 ,max(a(:),0)];
You can generalize this method for any number of elements in linspace function (not necessarily just 3). Note that readability will suffer, as the volume of code will increase:
j=4; % number of elements in each linspace
b=zeros(size(a,1),j); % create a solution matrix of size Nxj
b(:,1)=min(a(:),0); %first row
b(:,end)=max(a(:),0); % last row
temp=b(:,1)+b(:,end); % sum of the first and the last row
for i=2:j-1
b(:,i)=temp*(i-1)/(j-1); % fill in intermediate rows
end
Note, that in this method I loop over the number of elements in each linspace, but not through the array a
. With small j (like j=3 in your example) this will work way faster compared to the method with looping over the array a
(if you consider large arrays like a=rand(100000,1)
).