从向量中提取最小值、最大值和中值的最有效方法是啥

Posted

技术标签:

【中文标题】从向量中提取最小值、最大值和中值的最有效方法是啥【英文标题】:What's the most efficient way to extract min, max & median from a vector从向量中提取最小值、最大值和中值的最有效方法是什么 【发布时间】:2019-10-20 16:31:25 【问题描述】:

给定vector<T> vec...,假设 T 是数字类型之一,提取其最小值、最大值和中值的最佳方法是什么?我知道std::nth_elementstd::minmax_element,但如果一个接一个地调用它们,它们似乎会做多余的工作。

到目前为止,我想出的最好的想法是一次接一次地调用 std::nth_element 3 次。但这仍然需要 3N 比较,对吧?有没有办法重用之前迭代中完成的部分排序?

【问题讨论】:

最有效的方法是手动循环并同时计算。 @freakish 如何通过迭代元素获得中位数? std::partial_sort()? std::nth_element,然后是左半边的 std::min_element 和右半边的 std::max_element @DanielLangr, en.wikipedia.org/wiki/Quickselect 【参考方案1】:

使用std::nth_element 划分产生中位数,然后在左半部分使用std::min_element,在右半部分使用std::max_element

如果您需要它比这更快,请根据 std::nth_element 推出您自己的版本。

【讨论】:

【参考方案2】:

另一个选项是为std::nth_element 指定一个自定义比较,它捕获最小值和最大值。它可能最终会进行更多的比较和分支,因此在某些特定硬件上这可能会更慢,可能取决于缓存了多少数据等,因此 - 一如既往 - 如果你有理由关心,则进行基准测试,但是对于非空的vector a,该技术看起来像这样:

int min = a[0], max = a[0];
std::nth_element(a.begin(), a.begin() + n, a.end(),
    [&](int lhs, int rhs) 
        min = std::min(min, std::min(lhs, rhs));
        max = std::max(max, std::max(lhs, rhs));
        return lhs < rhs;
    );

在我的 (~10yo i5-660) HTPC 上使用 GCC 7.4 和 100 万个随机 ints 在 0 到 1000 之间,nth_element 的最小/最大比较比没有。

【讨论】:

您假设std::nth_element 将至少检查所有元素一次。这是合理的,但在技术上并不能保证。 @BiagioFesta:有趣的点;语言规范不保证,但我认为它在逻辑上不可能正常工作......? 当然,在理论计算机科学中有一些关于算法复杂性的证明。但是,C++ 标准甚至没有指定使用哪种算法来实现std::nth_element。如果我必须编写生产代码,我会自己实现完整的算法。 @BiagioFesta:这段代码的正确性与其时间复杂度不同。我会说这是不言而喻的,nth_element 重新排列输入数据实际上是不可能的,因为它需要为每个元素调用提供的比较函子 至少 一次,但是你'正确的是,当它多次调用它时,它可能会对性能产生不利影响/无法预测实现不透明时的严重程度;但在最坏的情况下,它会使比较加倍,所以没有大的-O 变化。不过可以衡量,这会让您了解是否值得担心。 在计算 (lhsmin = std::min(min, (ret?lhs:rhs);

以上是关于从向量中提取最小值、最大值和中值的最有效方法是啥的主要内容,如果未能解决你的问题,请参考以下文章

查找向量的向量的最大值/最小值

从长(且合理)稀疏向量中选择随机元素的最有效方法是啥?

从 ColumnDataSource 列中提取最大值和最小值

在c ++中不同行或列旁边的矩阵中搜索最小值和最大值的最快方法是啥

在 Scala 中创建最小堆的最简单和最有效的方法是啥?

在 PostgreSQL 中提取拆分字符串的最后一部分的最有效方法是啥?