Reputation: 35
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
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
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
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