【问题标题】:Why do I get quadratic time sorting using STL sort()?为什么我使用 STL sort() 进行二次时间排序?
【发布时间】:2012-10-10 02:59:57
【问题描述】:

我试图使用 STL sort() 函数按存储在地图中的值对对象向量进行排序。令我大吃一惊的是,我的算法是在二次时间中运行的。我尽可能地简化了它,试图找出一个明显的错误,但无济于事。这是简化版:

#include <map>
#include <vector>
#include <algorithm>

using namespace std;

struct a{
  map<a*,float> vals;
  bool operator()(a* a1, a* a2){return (vals[a1]>vals[a2]);}
  void asort();
};

void a::asort(){
  vector<a*> v;
  map<a*,float>::iterator it = vals.begin();
  for(;it!=vals.end();it++){v.push_back((*it).first);}
  sort(v.begin(),v.end(),*this);
}

int main(){
  a a0;
  int imax=8000;
  for(int i=0;i<imax;i++){a0.vals[new a]=rand();}
  a0.asort();
}

当我运行 imax=2000、4000、8000 时,分别需要大约 1s、4s、18s。这怎么可能?为什么我没有得到预期的 imax*log(imax) 依赖?我对C ++的经验有限,请帮助!谢谢!

更新:感谢 Xeo、Rick 和所有回复的人。正如 Xeo 和 Rick 解释的那样,问题在于比较器(在我的情况下是 struct a 与包含值的映射)在每次比较时都会被复制,因此计算复杂度 O(imax^2 log(imax))。我可以看到的一种解决方法(因此对我的代码所做的更改很少)是使用指向映射的指针,即map&lt;a*,float&gt;* vals,而不是map&lt;a*,float&gt; vals。然后就避免了地图复制,复杂度又回到O(imax log(imax))。非常感谢!

【问题讨论】:

  • 你确定时间是被排序而不是其他计算消耗的吗?
  • 你真的需要在地图上调用排序吗(已经排序)
  • @FamZheng:没有其他计算,时间是你看到的代码
  • @Rick:代码不是对地图进行排序,而是基于这些指针映射到的浮点数的指针向量。
  • @Rick:我需要按地图的值排序。它是如何排序的?我不明白。你的意思是我可以在不使用 sort() 的情况下拥有我的向量 v?

标签: c++ sorting stl quadratic


【解决方案1】:

std::sort 取谓词按值,意思是

sort(v.begin(),v.end(),*this);
//                     ^^^^^

将复制包含的地图。

接下来,您将在比较期间进行两次地图查找,即O(log N),而预计pred(a,b) 是一个常量操作。

您可以通过为 std::sort 定义单独的比较器并使用 std::unordered_map (C++11) 来解决此问题。

【讨论】:

  • @Mark:在a::operator() 内部,当从a* 映射到float 时,您会得到映射查找。 O(n) 如何支持更长的输入? vals.size() == n 在 OP 的代码中,意味着复制和查找的时间更长。
  • 你的意思是我从复制地图中得到了额外的 N 因子?为什么它会在每次比较时创建地图副本?抱歉,我很困惑为什么会得到〜O(N ^ 2)
  • @Flash:看看我回答中的std::sort链接。第三个参数是Compar comp,它将复制您作为第三个参数传递的任何内容。在这种情况下,即*this,其中包含大小等于imax 的映射,因此会导致O(imax) 复制。 :)
  • 我同意第三个参数至少被复制一次。但是,如果我理解正确,O(imax^2) 只能来自在每次比较中花费额外的 O(imax) 因子。那为什么每次都复制地图呢?
  • @FlashCards:请记住,您正在operator() 内部进行地图查找,其中添加了O(log imax)。此外,您的函数似乎不是完全二次的。
【解决方案2】:

地图已经排序。 std::sort 可能基于 Quicksort,其最坏情况下的性能是在输入预排序时。

【讨论】:

  • 向量根据a* 映射到的浮点数进行排序。此外,std::sort has a specified complexity of O(N log N),通常使用 introsort 实现,这是一种根据输入进行调整的算法。
  • std::sort 更复杂,它是插入排序/快速排序 IIRC 的组合(你可以看看)。但是你会认为简单地返回的 map 会有一个重载......
  • @Rick,这将取决于具体的实现。这个问题没有提到编译器。
  • 不,它没有,但您可以查看您的发行版,这是我的观点(抱歉不清楚)。
  • @Xeo,该规范是针对平均情况还是最差情况?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-09-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多