Prospective approach and Solution Code
Seems like the posted problem would be a good fit for accumarray
-
%// Starting indices of each "group"
start_ind = find(diff([0 ; a(:)]))
%// Setup IDs for each group
id = zeros(1,numel(a)) %// Or id(numel(a))=0 for faster pre-allocation
id(start_ind) = 1
%// Use accumarray to get the products of elements within the same group
out = accumarray(cumsum(id(:)),a(:),[],@prod)
For a non monotonically increasing input, you need to add two more lines of code -
[~,sorted_idx] = ismember(sort(start_ind),start_ind)
out = out(sorted_idx)
Sample run -
>> a
a =
2 2 3 5 11 11 17 4 4 1 1 1 7 7
>> out.'
ans =
4 3 5 121 17 16 1 49
Tweaky-Squeaky
Now, one can make use of logical indexing
to remove find
and also use the
faster pre-allocation scheme to give the proposed approach a super-boost and give us a tweaked code -
id(numel(a))=0;
id([true ; diff(a(:))~=0])=1;
out = accumarray(cumsum(id(:)),a(:),[],@prod);
Benchmarking
Here's the benchmarking code that compares all the proposed approaches posted thus far for the stated problem for runtimes -
%// Setup huge random input array
maxn = 10000;
N = 100000000;
a = sort(randi(maxn,1,N));
%// Warm up tic/toc.
for k = 1:100000
tic(); elapsed = toc();
end
disp('------------------------- With UNIQUE')
tic
ua = unique(a);
out = ua.^histc(a,ua);
toc, clear ua out
disp('------------------------- With ACCUMARRAY')
tic
id(numel(a))=0;
id([true ; diff(a(:))~=0])=1;
out = accumarray(cumsum(id(:)),a(:),[],@prod);
toc, clear out id
disp('------------------------- With FOR-LOOP')
tic
b = a(1);
for k = 2:numel(a)
if a(k)==a(k-1)
b(end) = b(end)*a(k);
else
b(end+1) = a(k);
end
end
toc
Runtimes
------------------------- With UNIQUE
Elapsed time is 3.050523 seconds.
------------------------- With ACCUMARRAY
Elapsed time is 1.710499 seconds.
------------------------- With FOR-LOOP
Elapsed time is 1.811323 seconds.
Conclusions: The runtimes it seems, support the idea of accumarray
over the two other approaches!