为啥 std::nth_element 返回 N < 33 个元素的输入向量的排序向量?

Posted

技术标签:

【中文标题】为啥 std::nth_element 返回 N < 33 个元素的输入向量的排序向量?【英文标题】:Why does std::nth_element return sorted vectors for input vectors with N < 33 elements?为什么 std::nth_element 返回 N < 33 个元素的输入向量的排序向量? 【发布时间】:2015-02-16 19:14:32 【问题描述】:

我正在使用std::nth_element 来获取向量百分位数的(大致正确的)值,如下所示:

double percentile(std::vector<double> &vectorIn, double percent)

    std::nth_element(vectorIn.begin(), vectorIn.begin() + (percent*vectorIn.size())/100, vectorIn.end());

    return vectorIn[(percent*vectorIn.size())/100];
  

我注意到对于最多 32 个元素的 vectorIn 长度,向量会完全排序。从 33 个元素开始,它永远不会排序(如预期的那样)。

不确定这是否重要,但该函数位于通过 Matlab 使用“Microsoft Windows SDK 7.1 (C++)”编译的“(Matlab-)mex c++ 代码”中。

编辑:

另请参阅以下直方图,其中显示了传递给函数的 1e5 个向量中最长排序块的长度(向量包含 1e4 个随机元素,并计算了一个随机百分位数)。请注意非常小的值处的峰值。

【问题讨论】:

该函数进行部分排序以返回您请求的值。它做多少部分排序取决于实现。 不,与墨西哥无关,但很酷的问题。 绘图左侧的尖峰看起来很像随机向量中最长连续子序列的长度直方图。这可能对应于随机选择的百分位值的一小部分,它非常接近向量的末端,以至于最长的子序列位于向量中从未被 nth_vector 触及的部分。但这只是猜测。 @rici:好主意,但我检查了它,但事实并非如此。对于向量以这些非常短的排序序列结束的运行,相应的百分位数也均匀分布在 0 到 100 之间。 【参考方案1】:

这会因标准库实现而异(可能因其他因素而异),但总体而言:

std::nth_element 可以根据需要重新排列输入容器,前提是第 nth_element 位于位置 n,并且容器在位置 n 处被分区。

对于小型容器,执行完全插入排序通常比快速选择更快,即使它不可扩展。

由于标准库作者通常会选择最快的解决方案,因此大多数 nth_element 实现(以及就此而言,排序实现)使用自定义算法用于小输入(或递归底部的小段),这可能会排序容器比看起来必要的更积极。对于标量值的向量,插入排序非常快,因为它最大限度地利用了缓存。使用流式扩展,可以通过并行比较进一步加快速度。

顺便说一句,你可以通过只计算一次阈值迭代器来节省少量的计算,这可能更具可读性:

double percentile(std::vector<double> &vectorIn, double percent)

    auto nth = vectorIn.begin() + (percent*vectorIn.size())/100;
    std::nth_element(vectorIn.begin(), nth, vectorIn.end());
    return *nth;

【讨论】:

还不能投票,所以首先:谢谢。你在我添加的地块上有什么 cmets 吗? @stack_horst:漂亮的图表。但是变量太多,我不知道Windows std:: 实现的细节。您是在整个向量中搜索排序的运行还是直到分区点?随机百分位数的范围是多少?它是否仅限于整数百分比? 我正在搜索整个向量。 1e5 个输入向量每个都有 1e4 个 double 值,随机分布在 0 到 100 之间,百分位数是 0 到 100 之间的 double rand。

以上是关于为啥 std::nth_element 返回 N < 33 个元素的输入向量的排序向量?的主要内容,如果未能解决你的问题,请参考以下文章

了解 nth_element

使用 std::nth_element 对 arma::mat 的行进行排序

为啥使用指针的 STL 算法比 std::vector 迭代器快得多?

BeautifulSoup nth-of-type 返回空列表。 Soup.select()[n -1] 返回元素。为啥?

为啥 Popen.communicate() 返回 b'hi\n' 而不是 'hi'?

为啥返回变量的值会改变?