electricsurge31
electricsurge31

Reputation: 35

Divide array into blocks where each block is a longest ascending (increasing) sequence

I'm trying to take a vector and split into sections. the code requires me to create a separate vector when the ascending order stops like this: [3, 5, 9, 21, 27, 15, 10, 13] will get divided into three blocks [3 5 9 21 27], [15] and [10, 13].

I'm not supposed to use a loop so it would be helpful if somebody is able to help me out. Thanks.

Upvotes: 1

Views: 238

Answers (3)

Luis Mendo
Luis Mendo

Reputation: 112689

It can be done very easily with mat2cell:

x = find(diff(a)<0);
result = mat2cell(a, 1, [x(1) diff(x) numel(a)-x(end)]);

If you want a one-liner:

result = mat2cell(a, 1, diff(find(diff([inf a -inf])<0)));

Upvotes: 1

user2271770
user2271770

Reputation:

The diff function calculates the difference between adjacent elements of an array. An element that is negative in a diff result points to the place where the ascending order is "broken".

So:

 v  = [3, 5, 9, 21, 27, 15, 10, 13];
 dv = diff(v);
 nb = sum(dv <= 0);  % number of blocks
 b  = cell(1, nb);   % container of blocks

 ix  = find(dv <= 0);  % start indices of blocks
 ix0 = 1;
 for k = 1:nb
         ix1 = ix(k);
         b{k} = a(ix0:ix1);
         ix0 = ix1 +1 ;
 end;

My opinion is that is hard avoid using loops, however, the code above is much more "gentle" regarding the computation load compared to the brute force method of finding blocks element by element.

Upvotes: 0

Robert Seifert
Robert Seifert

Reputation: 25232

No loops, one line, I hope it's alright ;)

a = [3, 5, 9, 21, 27, 15, 10, 13];

output = accumarray( cumsum([0; diff(a(:))] < 0)+1, a, [], @(x) {x} )

some explanations:

%// find out where the vector decreases:
da = [0; diff(a(:))]
%// and create a mask
mask = da < 0
%// sum the mask up cumulatively and add +1
subs = cumsum(mask) + 1
%// use accumarray to gather everything
output = accumarray(subs,a,[],@(x) {x})

If there is subsequent the same number, like here:

a = [3, 5, 9, 21, 27, 27, 15, 10, 13];

the solution above counts the second 27 to the first group, if you want it to be a separate group, change the mask to:

mask = da <= 0

Upvotes: 2

Related Questions