查找小于其右侧某个元素的每个元素

Posted

技术标签:

【中文标题】查找小于其右侧某个元素的每个元素【英文标题】:Find each element that is less than some element to its right 【发布时间】:2014-06-02 07:07:51 【问题描述】:

我需要找到一个向量中小于其后的多个元素之一的元素。在循环中很容易做到:

x = some_vector_values;
for m = 1 : length(x)
  if( any( x(m+1:end) > x(m) )
    do_such_and_such;
  end
end

但是速度快要死了。我正在挠头,试图想出一个有效的解决方法,但我一无所获。数组长度可以达到数千,我需要为许多不同的数组执行此操作。

【问题讨论】:

不能先对向量进行排序吗?那么你肯定知道特定数字后面的数字比那个小(大)。 一些示例输入和输出来展示您的要求? 尝试运行分析器,是什么导致了主要的 cpu 时间、循环/条件或“do_such_and_such”? x 是什么维度? 我同意@NKN。请注意,sort 也可以返回已排序元素的原始索引。 @Daniel 这是循环中的if 比较。 【参考方案1】:

单行版本

comparisons = any(triu(bsxfun(@gt,x(:).',x(:))),2)

【讨论】:

对于大型向量 (numel(x)=10000),此解决方案较慢。 @Daniel 正如@natan 所指出的那样,这是因为您在那里拉伸bsxfun 谢谢。即使在最坏的情况下并不理想,但在大多数情况下这要快得多并且非常有帮助。 @AnonSubmitter85 太棒了!我猜数据量越小越快! 是的,但几千大小的向量是最常见的。我仍在查看其他答案,以了解它在较大案例中的表现。【参考方案2】:

这使用divide-and-conquer 方法(类似于二分查找):

    求向量的最大值。 接受其左侧的所有元素,而拒绝最大值本身。 对于最大值右侧的那些元素,应用第 1 步。

虽然我没有仔细分析,但我认为平均复杂度是O(n),或者最多O(n 记录 n)。内存是O(n)。

结果是一个逻辑向量 ind,其中包含接受元素的 true 和拒绝元素的 false。最终结果将是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

while 条件更改为while s &lt; n 可以稍微减少运行时间,因为最后一个元素总是被拒绝。

【讨论】:

是的!这一定是更高数据的逻辑! +1 我猜这是O(n log(n)) 的整体运行时间,而不是O(log(n))。我认为我的解决方案是O(n) @BasSwinckels 你是对的,找到max 引入了一个线性因子。已更正 @BasSwinckels 再想一想,也许是 O(n)。平均而言,我计算 n 个元素的最大值,然后是 n/2 个元素,然后是 n/4 个元素......总和为 2*n 你可能是对的,它应该是O(n) 在最佳情况下(最后最大值)和平均情况下(就像你解释的那样),但O(n^2) 在最坏情况下(当 x 严格减少)。【参考方案3】:

你的算法太慢了,因为if any(...)必须在第一次迭代中检查n项目,然后在第二次迭代中检查n-1项目......直到在最后一次迭代中检查单个项目。总的来说,它必须进行大致的n^2/2 比较,因此它的运行时间是输入向量长度的二次函数!

一种在时间和内存上呈线性的解决方案可能是首先计算一个从该点到结束具有最大值的向量,该向量可以通过一次反向传递来计算 (您可以将其称为反向累积最大值,cannot be vectorized)。在此之后,该向量直接与x(未经测试)进行比较:

% 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

如果您不关心 do_such_and_such 的执行顺序,这些 for 循环甚至可以像这样组合:

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

【讨论】:

谢谢。我得到了几个很好的答案,但这一个既简单又最快。非常有帮助,而不仅仅是针对这个特定问题;我觉得我学到了一些东西以供将来参考。【参考方案4】:

这应该是一个需要 O(n) 时间和 O(n) 内存的算法:将数组中的最后一个元素标记为最大元素。向后迭代数组。每当您有一个小于最大值的元素时,请保存它。否则,它将成为您的新最大值。这应该可以让您一次性获得所需的所有元素。

【讨论】:

【参考方案5】:

如果您想查找小于其右侧某个元素的元素,您也可以这样做:

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

汉克尔矩阵将在沿行向下时显示右侧的元素。

然后您可以使用索引或 true/false 来遍历 for 循环并执行一些操作。这是一个例子:

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

【讨论】:

此方案使用O(n^2)内存。【参考方案6】:

@NKN 说得对。排序。

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

祝你好运

【讨论】:

也许我遗漏了一些明显的东西,但我不知道这是如何工作的。有两个标准:1)A > B,2)A在B的右边。 @BasSwinckels - 对于已编译的软件是的。对于解释 - 也许不是。内置的排序可能已编译,您的脚本 - 不多。我已经看到编译的运行速度比解释的快 1000 倍,但你的里程可能会有所不同。当形式为 O(n log(n)) 为 O( 1000*n log(1000*n) / n log(n) ) 或大约 3000 * O(n log( n))。内置程序正在快速尖叫。 Matlab 现在有一个相当不错的 JIT,尤其是对于简单的循环,它应该在编译代码的一小部分范围内。对于大数组,这个常数因子可能小于 log(n)。 这个答案不满足问题的标准。

以上是关于查找小于其右侧某个元素的每个元素的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode 315. 计算右侧小于当前元素的个数

leetcode 315 计算右侧小于当前元素的个数

leetcode [315. 计算右侧小于当前元素的个数]

每个元素小于某个特定数字的子数组的总和

使用 Functor / Predicate 查找向量中小于其前任的第一个元素

二分搜索算法