【问题标题】:Efficient find of first element of a vector that meets a logical condition in MatlabMatlab中满足逻辑条件的向量的第一个元素的有效查找
【发布时间】:2017-07-17 14:42:08
【问题描述】:

我需要找到满足条件 A(i)

i = find(A <= b, 1)

Matlab 似乎在做类似的事情:

X = A <= b
find(X, 1)

计算 X 的所有元素是浪费:一旦遇到满足 A(i)

for i = 1 : length(A)

    if A(i) <= b

        break

    end

end

但迭代代码比向量化代码还要慢。

有什么方法可以更有效地执行此查找吗?

【问题讨论】:

  • 我认为比创建整个逻辑向量 X 更快的唯一方法是用 C 语言编写函数。除非您已将其确定为程序中的瓶颈,我会保持原样。
  • 您的条目是随机的吗?基本上,i 可以为 1,numel(A) 也可以吗?通常,如果您做出额外的假设,例如,您可以加快速度。仅在 A 的特定区域中搜索,等等。
  • 启发式方法可能有很大帮助,具体取决于数据。仅测试前 n 个条目,如果解决方案不在这些条目中,则仅恢复检查所有条目。或者,您可以尝试通过逐块检查数据来组合循环方法和查找方法。

标签: matlab performance find vectorization


【解决方案1】:

我不确定您是如何得出循环代码比矢量化代码慢的结论,但这里有一个基准和一些结果背景!

我使用的是 2015b,所以这可能会利用 Matlab 的new(ish) JIT compiler


function benchie()
    A = rand(1e9, 1);
    b = 0.01;
    f = @() original(A,b);
    g = @() usingloop(A,b);
    disp(['Original with logical A <= b:    ', num2str(timeit(f)), ' sec']);
    disp(['Looping until found, then break: ', num2str(timeit(g)), ' sec']);
end
% Both of these functions return the same value for idx
function idx = original(A,b)
    idx = find(A <= b, 1);
end    
function idx = usingloop(A,b)
    for idx = 1:length(A)
        if A(idx) <= b
            break
        end
    end
end

输出

Original with logical A <= b:    0.83666 sec

Warning: The measured time for F [function g] may be inaccurate because it is running
too fast. Try measuring something that takes longer. 
> In timeit (line 158)
  In benchie (line 7) 
Looping until found, then break: 1.8043e-06 sec

总结

使A 与我的RAM 允许的大小(8GB)一样大,usingloop 方法仍然太快,timeit 无法认为自己准确!这两个函数都给出了相同的答案,我认为usingloop 要快几个数量级。

我很想包含一个图表,显示不同大小数组的时间比较,但不能因为usingloop 太快...


注意:我使用idx 作为我的变量,而不是i。这是因为默认情况下i 是复数常量sqrt(-1),并且在重写时,您将立即招致(小)性能损失!


编辑:

我运行了另一个测试,A = 1e8:-1:1;b 在通过 A 的十分之一的值处查看向量化的开销何时超过了进一步循环到数组中所需的时间。下面是输出:

您可以看到逻辑比较的开销几乎是所有时间所需的,并且无论位置如何,该向量的实际索引都非常小。相比之下,一旦所需元素进入数组的路径很短,循环方法就很糟糕。

鉴于 MATLAB 在使用矢量化和逻辑索引进行快速计算方面享有盛誉,我将回应 @beaker 的评论,即自定义(编译的)C 函数可能是查看原始方法改进的唯一方法。

【讨论】:

  • 如果我可以提出一些建议,首先,使用随机值,你真的不知道第一个值是停止循环还是最后一个值。让第一个A(idx)&lt;=b 出现在数组的中间可能更有指导意义,比如A = 1e9:-1:1; b = 1e9/2;。其次,随着数组A 的大小增加,看看这两种方法的时序如何变化也会很有趣。 A 必须有多大才能使循环开销小于不必要地处理整个数组所花费的时间?
  • 对于您的第一点:我同意,我确实运行了几次输出idx 的测试,以确保它不会太早,结果似乎具有代表性,但(还)没有确凿的数据。对于您的第二点:请参阅我关于想要制作情节的说明,循环方法太快而无法可靠地计时,因此尝试在较小的数组上进行比较几乎没有意义......
  • A[0,1)b = 0.01 范围内使用随机值,您将平均达到A(idx) &lt;= bidx == 100 附近的值。这可能就是为什么您如此快速地完成循环版本的原因。您平均只达到了阵列的 0.00001%。
  • 我在测试中使用了不同的 b 值,但已采纳您的建议并进行了更彻底的调查,请参阅编辑。
  • 太棒了!看起来它提供了更好的比较。
猜你喜欢
  • 2014-09-25
  • 1970-01-01
  • 2013-05-28
  • 2019-03-09
  • 2021-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-13
相关资源
最近更新 更多