Linspace applied on array [duplicate]

2019-07-29 04:04发布

This question already has an answer here:

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?

3条回答
可以哭但决不认输i
2楼-- · 2019-07-29 04:18

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)).

查看更多
狗以群分
3楼-- · 2019-07-29 04:34

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
查看更多
Luminary・发光体
4楼-- · 2019-07-29 04:36

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
查看更多
登录 后发表回答