Is it possible to vectorize this?

조회 수: 9 (최근 30일)
Joakim Hansen
Joakim Hansen 2018년 11월 6일
댓글: Joakim Hansen 2018년 11월 6일
A = [starts ends];
b = zeros(size(A,1),1);
c = zeros(size(A,1),1);
for i = 1:1:size(A,1)
b(i) = max(x(A(i,1):A(i,2)));
c(i) = min(x(A(i,1):A(i,2)));
end
starts, ends and x are column vectors. The intervals does not overlap.
  댓글 수: 8
Jan
Jan 2018년 11월 6일
편집: Jan 2018년 11월 6일
If this is an important and time-critical part of your code, I recommend to install a C-compiler and use a C-Mex function. Did you try parfor?
Joakim Hansen
Joakim Hansen 2018년 11월 6일
It's just a part of some coursework where vectorization is preferred over loops, and I wasn't able to vectorize that loop. If the only solution is some relatively advanced stuff like C-Mex, I will just go with the loop.

댓글을 달려면 로그인하십시오.

채택된 답변

Christine Tobler
Christine Tobler 2018년 11월 6일
There is no general way to vectorize this. If the starts and ends vector define intervals that are not overlapping, and that cover every index in x, you can do the following:
ind = zeros(length(x), 1);
for i=1:size(A, 1)
ind(starts(i):ends(i)) = i;
end
% ind(j) gives the number of the interval that j (and x(j)) are in.
b = accumarray(ind, x(:), [], @max);
c = accumarray(ind, x(:), [], @min);
Depending on where starts and ends are coming from, maybe you can construct ind in a vectorized format from there?

추가 답변 (3개)

Bruno Luong
Bruno Luong 2018년 11월 6일
For generic x, A, no there is no direct way.
What you might do is decomposing the set of intervals in bigger set of non-overlapping intervals, find MIN and MAX in subintervals, which is faster then group them together depending how the original interval is composed of subintervals.
Not sure it's more efficient, but this conclusion might depends on how the overlapping/non-overlapping of the intervals are.
  댓글 수: 4
Bruno Luong
Bruno Luong 2018년 11월 6일
No, I have not say anything about sorting x.
Sorting A break points yes.
Bruno Luong
Bruno Luong 2018년 11월 6일
'None of the intervals are overlapping.'
So I think the bottleneck is that MATLAB make a duplication of subdata for every iteration. In that case you might have to program MEX file to avoid such copying.
If you are running R2018, One intermediate alternative is using my on-table share data mex file as I show in this thread

댓글을 달려면 로그인하십시오.


Guillaume
Guillaume 2018년 11월 6일
First, note that you should never use length on a matrix. You're using length as synonymous to the number of rows but if A has more columns than rows then it's going to be the number of columns. So use size with the proper dimension index.
There is no straightforward way to vectorise your code. It can be done at the expense of a large temporary array and a fair bit of juggling, so I'm not convinced it's worthwile. Here is one way:
mask = zeros(numel(starts), numel(x) + 1); %one more column than elements in x as we will be using ends+1 as index
mask(sub2ind(size(mask), 1:numel(starts), starts')) = 1;
mask(sub2ind(size(mask), 1:numel(ends), ends' + 1)) = -1;
mask = cumsum(mask(:, 1:end-1), 2);
mask(mask == 0) = NaN;
[c, b] = bounds(mask .* x', 2); %requires R2017a. In previous versions use separate calls to min/max
This may be slightly faster but will output row vectors for c and b instead of column vectors:
mask = zeros(numel(x) + 1, numel(starts));
mask(sub2ind(size(mask), starts', 1:numel(starts))) = 1;
mask(sub2ind(size(mask), ends' + 1, 1:numel(ends))) = -1;
mask = cumsum(mask(:, 1:end-1));
mask(mask == 0) = NaN;
[c, b] = bounds(mask .* x); %requires R2017a. In previous versions use separate calls to min/max

Bruno Luong
Bruno Luong 2018년 11월 6일
편집: Bruno Luong 2018년 11월 6일
The orginal method is quite fast, for random intervals (they cover 50-60% of x) on my computer it takes 1.7 ms in average. Guillaume's method takes forever. I add my own method called Scanning here.
Orginal algo : t=0.001669 [s]
Scanning algo : t=0.002096 [s]
Guillaume algo: t=1.077173 [s]
Christine algo: t=0.026156 [s]
Test code
ntest = 100;
t = zeros(3,ntest);
for j = 1:ntest
x = rand(775000,1);
n = 79;
b = randperm(length(x),2*n);
A = reshape(sort(b),2,[])';
% Orginal method
tic
b = zeros(size(A,1),1);
c = zeros(size(A,1),1);
for i = 1:1:size(A,1)
b(i) = max(x(A(i,1):A(i,2)));
c(i) = min(x(A(i,1):A(i,2)));
end
t(1,j) = toc;
% Scanning method, assuming Interval are sorted
tic
b = zeros(n,1);
c = zeros(n,2);
for k=1:n
is = A(k,1);
minx = x(is);
maxx = minx;
for i=is+1:A(k,2)
if x(i) < minx
minx = x(i);
elseif x(i) > maxx
maxx = x(i);
end
end
b(k) = maxx;
c(k) = minx;
end
t(2,j) = toc;
% Guillaume method
tic;
m = size(x,1);
mask = zeros(n, m + 1); %one more column than elements in x as we will be using ends+1 as index
mask(sub2ind(size(mask), 1:n, A(:,1)')) = 1;
mask(sub2ind(size(mask), 1:n, A(:,2)' + 1)) = -1;
mask = cumsum(mask(:, 1:end-1), 2);
mask(mask == 0) = NaN;
[c, b] = bounds(mask .* x', 2);
t(3,j) = toc;
clear mask
% Modification of Christine Method
tic
ind = zeros(length(x), 1);
for i=1:size(A, 1)
ind(A(i,1):A(i,2)) = i;
end
% ind(j) gives the number of the interval that j (and x(j)) are in.
tf = ind > 0;
x = x(:);
b = accumarray(ind(tf), x(tf), [], @max);
c = accumarray(ind(tf), x(tf), [], @min);
t(4,j) = toc;
end
t = mean(t,2);
fprintf('Orginal algo : t=%f [s]\n', t(1));
fprintf('Scanning algo : t=%f [s]\n', t(2));
fprintf('Guillaume algo: t=%f [s]\n', t(3));
fprintf('Christine algo: t=%f [s]\n', t(4));
  댓글 수: 1
Guillaume
Guillaume 2018년 11월 6일
Guillaume's method takes forever
I'm not surprised by that. It's got to fill a 775001 x 79 matrix and then do a cumsum on that.

댓글을 달려면 로그인하십시오.

카테고리

Help CenterFile Exchange에서 Performance and Memory에 대해 자세히 알아보기

태그

제품


릴리스

R2018b

Community Treasure Hunt

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

Start Hunting!

Translated by