2 views (last 30 days)

Show older comments

Hello, I have 2 vectors of same size let's say a = [3 7 19 22] and b = [5 10 20 24]

And from those 2 vectors I want to generate without a loop a single vector:

v = [a(1):b(1) a(2):b(2) ... a(end):b(end)].

so here v=[3 4 5 7 8 9 10 19 20 22 23 24].

I found linspaceNDim(a,b,N) on File Exchange Linearly spaced multidimensional matrix without loop but it doesn't work here as I dont want a fixed number of values between a(i) and b(i), but only block-consecutive values : a(i):b(i). So here N is changing at each index i.

I tried a:.b but this syntax doesn't work on Matlab.

Thanks for you help.

Amro
on 20 Jun 2014

José-Luis
on 16 Jun 2014

Edited: José-Luis
on 17 Jun 2014

Any particular reason you don't want to use a loop? This is one of those cases where you should use one, if performance is an issue. Arrayfun() is just syntactic sugar around a loop. Plus the added overheads of invoking cell arrays:

a = [3 7 19 22];

b = [5 10 20 24];

tic

your_mat = cell2mat(arrayfun(@(x,y) x:y,a,b,'uniformoutput',false));

toc

tic

numEl = b - a;

pos = 1;

your_data = NaN * ones(sum(numEl),1);

for ii = 1:numel(numEl)

your_data(pos:pos+numEl(ii)) = a(ii):b(ii);

pos = pos + numEl(ii) + 1;

end

toc

Elapsed time is 0.000602 seconds.

Elapsed time is 0.000038 seconds.

EDIT

I had made a mistake, the pre-allocation should read:

your_data = NaN * ones(sum(numEl) + numel(numEl),1);

EDIT

A bit cleaner:

vals = reshape(sort(randperm(10^6,1000)),2,[]);

a = vals(1,:);

b = vals(2,:);

tic

offset = b - a;

numVal = sum(offset + 1);

pos = 1;

your_data = ones(numVal,1);

for ii = 1:numel(offset)

your_data(pos:pos+offset(ii)) = a(ii):b(ii);

pos = pos + offset(ii) + 1;

end

toc

José-Luis
on 17 Jun 2014

I had done the pre-allocation wrong. Please replace by:

your_data = ones(b(end),1);

or

your_data = (sum(numEl) + numel(numEl),1);

Andrei Bobrov
on 16 Jun 2014

Edited: Andrei Bobrov
on 17 Jun 2014

v = a(1):b(end);

v = v(any(bsxfun(@ge,v,a.')&bsxfun(@le,v,b.')));

other variant

zo = zeros(b(end) - a(1) + 2,1);

zo(a - a(1) + 1) = 1;

zo(b - a(1) + 2) = -1;

t = cumsum(zo(1:end-1)) > 0;

out = a(1):b(end);

v = out(t);

Sean de Wolski
on 17 Jun 2014

Edited: Sean de Wolski
on 17 Jun 2014

Azzi Abdelmalek
on 16 Jun 2014

Edited: Azzi Abdelmalek
on 16 Jun 2014

cell2mat(arrayfun(@(x,y) x:y,a,b,'un',0))

jyloup p
on 17 Jun 2014

José-Luis
on 17 Jun 2014

That's not a fair comparison. If you generate your limits like that, with common intervals, then the for loop produces garbage. Also the pre-allocation becomes meaningless and the output keeps changing size, leading to poor performance.

If the data is set with non-overlapping intervals, like in the original question, then the loop is still faster:

vals = reshape(sort(randperm(10^6,1000)),2,[]);

a = vals(1,:);

b = vals(2,:);

tic

offset = b - a;

numVal = sum(offset + 1);

pos = 1;

your_data = ones(numVal,1);

for ii = 1:numel(offset)

your_data(pos:pos+offset(ii)) = a(ii):b(ii);

pos = pos + offset(ii) + 1;

end

toc

tic

zo = zeros(b(end) - a(1) + 2,1);

zo(a - a(1) + 1) = 1;

zo(b - a(1) + 2) = -1;

t = cumsum(zo(1:end-1)) > 0;

out = a(1):b(end);

v = out(t);

toc

Elapsed time is 0.003596 seconds.

Elapsed time is 0.024514 seconds.

That being said, I am always impressed by Andrei's tricks.

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!