0

I want to apply a linespace function for each element of a vector, and create a whole vector in the end, is there some way convenient to do it?

Specifically,I want to create matrix B by matrix A without using a loop, where A = [1; 2; 3; 4] B = [1; 1; 2; 1; 2; 3; 1; 2; 3; 4]

As you can see, if the third element in the vector is 3, then the "third vector" in the output vector B will be [1:1:3]. The step I desired is fixed and will be exact 1 for any element.

Could anyone help me, please?

Meepo
  • 1
  • 1

3 Answers3

3

Because of the dynamic nature of B, there is no way you can do it without any loops. I would not shy away from loops in this case - especially for more recent versions of MATLAB with their improved JIT compiler. Loops are now quite competitive over vectorized operations depending on what exactly you want to do.

The easiest approach would be to loop over all values of A and the colon operator and go from 1 up to each value in A.

Something like this would work:

A = [1; 2; 3; 4];
B = [];

for v = 1 : numel(A)
    B = [B; (1:A(v)).'];
end

At each iteration, we find a value in A and create a vector that goes from 1 up to the value. We concatenate all of these into a single vector. Keep in mind that no error checking is performed so we're assuming that every value in A is greater than or equal to 1.

If you're keen on a one liner, it's possible to do this with arrayfun to create a bunch of cell arrays and squeeze them together, but performance wise this may not be the best solution:

A = [1; 2; 3; 4];
B = cell2mat(arrayfun(@(x) (1:x).', A, 'un', 0));

arrayfun takes in a function to operate on each input element in A, so the function would be to create a column vector from 1 up to the desired value. un is short for UniformOutput and it is set to 0 meaning that the output is not uniform, so we're going to output a bunch of cell arrays - one for each vector you've created. Finally, cell2mat squeezes all of the cell arrays together to create a single column vector. It's more elegant, but performance wise it may not be so as there is a lot of overhead.

rayryeng
  • 102,964
  • 22
  • 184
  • 193
  • Thanks! It's so kind for you to reply. I will use "cell2mat" in my code. – Meepo Apr 06 '18 at 14:54
  • @陈宇健 - 不用谢. Let me know if you need any more help. If not, when you're ready please accept one of our answers to let the community know you no longer need help in this regard. Good luck! – rayryeng Apr 06 '18 at 14:57
  • _there is no way you can do it without any loops_ Well, there are at least two ways :-P Not saying they are the best options, though... – Luis Mendo Apr 06 '18 at 19:04
3

Here are two ways that avoid loops.

  • Using bsxfun: this approach is memory- (and perhaps time-) inefficient:

    t = 1:max(A);
    B = nonzeros(bsxfun(@times, t.', bsxfun(@le, t.', t)));
    
  • Using cumsum: this is memory-efficient, but a little more convoluted:

    A = nonzeros(A); % remove zeros, if any
    B = ones(1, sum(A)); % initiallize
    B(cumsum(A(1:end-1))+1) = 1-A(1:end-1); % write appropriate values...
    B = cumsum(B).'; % ... so that the cumulative sum gives the desired output
    

A loop is probably the best way: simplest and clearest.

Luis Mendo
  • 110,752
  • 13
  • 76
  • 147
0

You can do it using a for loop as follows:

B = [];
for ii=1:size(A)
    B = [B;(1:A(ii))'];
end

Result

B = [1; 1; 2; 1; 2; 3; 1; 2; 3; 4]
ibezito
  • 5,782
  • 2
  • 22
  • 46