I need to find elements of a vector that are less than one of more elements that come after it. It\'s easy to do in a loop:
x = some_vector_values;
for m = 1
One-liner version
comparisons = any(triu(bsxfun(@gt,x(:).',x(:))),2)
This should be an algorithm that takes O(n) time and O(n) memory: Label the last element in your array the maximum element. Iterate backwards over the array. Whenever you have an element smaller than your maximum, save it. Otherwise, it becomes your new maximum. This should get you all of the elements you need with a single pass.
This uses a divide-and-conquer approach (similar to binary search):
Although I haven't done a careful analysis, I think average complexity is O(n), or at most O(n log n). Memory is O(n).
The result is a logical vector ind
that contains true
for accepted elements and false
for rejected elements. The final result would be x(ind)
.
x = [3 4 3 5 6 3 4 1];
n = numel(x);
ind = false(1,n); %// intiallization
s = 1; %// starting index of the part of x that remains to be analyzed
while s <= n %// if s > n we have finished
[~, m] = max(x(s:end)); %// index of maximum within remaining part of x
ind(s:s-2+m) = true; %// elements to its left are accepted
s = s+m; %// update start of remaining part
end
Running time could be reduced a little by changing the while
condition to while s < n
, because the last element is always rejected.
Your algorithm is so slow since if any(...)
has to check n
items on the first iteration, then n-1
items on the second iteration ... until checking a single item in the last iteration. Overal it thus has to do roughly n^2/2
comparisons, so its running time is quadratic as a function of the length of the input vector!
One solution that is linear in time and memory might be to first calculate a vector with the maximum from that point until the end, which can be calculated in one backwards pass
(you could call this a reversed cumulative maximum, which cannot be vectorized). After this, this vector is compared directly to x
(untested):
% calculate vector mx for which mx(i) = max(x(i:end))
mx = zeros(size(x));
mx(end) = x(end);
for i = length(x)-1:-1:1 % iterate backwards
mx(i) = max(x(i), mx(i+1));
end
for i = 1:length(x) - 1
if mx(i) > x(i)
do_such_and_such(i);
end
end
In case you don't care about the order in which do_such_and_such
is executed, these for loops can even be combined like so:
mx = x(end);
for i = length(x)-1:-1:1 % iterate backwards
if x(i) < mx
do_such_and_such(i);
end
mx = max(x(i), mx); % maximum of x(i:end)
end
If you would like to find elements that are less than some element to its right, you can do this also:
x = some_values'; % x should be a column vector to use this
h = hankel(x);
m = max(h,[],2);
f = find(x<m) %returns indices or f = (a<m) %returns true/false
The hankel matrix will show the elements to the right as it goes down the rows.
Then you can use the indices or true/false to iterate through a for loop and perform some operations. Here is an example:
x =
9
8
16
16
4
10
9
13
15
1
>> h = hankel(x)
h =
9 8 16 16 4 10 9 13 15 1
8 16 16 4 10 9 13 15 1 0
16 16 4 10 9 13 15 1 0 0
16 4 10 9 13 15 1 0 0 0
4 10 9 13 15 1 0 0 0 0
10 9 13 15 1 0 0 0 0 0
9 13 15 1 0 0 0 0 0 0
13 15 1 0 0 0 0 0 0 0
15 1 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0
>> m = max(h,[],2)
m =
16
16
16
16
15
15
15
15
15
1
>> f = find(a<m)
f =
1
2
5
6
7
8
@NKN has it right. Sort.
x = some_vector_values;
[Y,I]=sort(x); %sort in order, get indices
dy=gradient(Y); %get difference vector same size as input vector
ind=find(dy~=0);%ignore places that are equal to the value of interest
for m = 1 : length(ind)
do_such_and_such to Y(ind(m));
end
Best of luck