How to generate a single vector of block-consecutive values from 2 vectors of same size without a loop ?
조회 수: 2 (최근 30일)
이전 댓글 표시
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
Amro
2014년 6월 20일
here is a similar question on Stack Overflow: Vectorized array creation from a list of start/end indices
채택된 답변
José-Luis
2014년 6월 16일
편집: José-Luis
2014년 6월 17일
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
José-Luis
2014년 6월 17일
I had done the pre-allocation wrong. Please replace by:
your_data = ones(b(end),1);
or
your_data = (sum(numEl) + numel(numEl),1);
추가 답변 (6개)
Andrei Bobrov
2014년 6월 16일
편집: Andrei Bobrov
2014년 6월 17일
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
Sean de Wolski
2014년 6월 17일
편집: Sean de Wolski
2014년 6월 17일
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.
Azzi Abdelmalek
2014년 6월 16일
편집: Azzi Abdelmalek
2014년 6월 16일
cell2mat(arrayfun(@(x,y) x:y,a,b,'un',0))
댓글 수: 1
jyloup p
2014년 6월 17일
댓글 수: 2
José-Luis
2014년 6월 17일
편집: José-Luis
2014년 6월 17일
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.
참고 항목
카테고리
Help Center 및 File Exchange에서 Logical에 대해 자세히 알아보기
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!