How to generate a single vector of block-consecutive values from 2 vectors of same size without a loop ?
2 Ansichten (letzte 30 Tage)
Ältere Kommentare anzeigen
jyloup p
am 16 Jun. 2014
Kommentiert: Amro
am 20 Jun. 2014
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.
1 Kommentar
Amro
am 20 Jun. 2014
here is a similar question on Stack Overflow: Vectorized array creation from a list of start/end indices
Akzeptierte Antwort
José-Luis
am 16 Jun. 2014
Bearbeitet: José-Luis
am 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
2 Kommentare
José-Luis
am 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);
Weitere Antworten (6)
Andrei Bobrov
am 16 Jun. 2014
Bearbeitet: Andrei Bobrov
am 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);
0 Kommentare
Sean de Wolski
am 17 Jun. 2014
Bearbeitet: Sean de Wolski
am 17 Jun. 2014
There's also FEX:mcolon which does exactly what you're looking for and has a mex implementation that may very well be the fastest.
1 Kommentar
Azzi Abdelmalek
am 16 Jun. 2014
Bearbeitet: Azzi Abdelmalek
am 16 Jun. 2014
cell2mat(arrayfun(@(x,y) x:y,a,b,'un',0))
1 Kommentar
jyloup p
am 17 Jun. 2014
2 Kommentare
José-Luis
am 17 Jun. 2014
Bearbeitet: José-Luis
am 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.
Siehe auch
Kategorien
Mehr zu Logical finden Sie in Help Center und File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!