C++ 是什么使桶排序功能变慢?

C++ 是什么使桶排序功能变慢?,c++,algorithm,performance,stl,C++,Algorithm,Performance,Stl,该函数定义为 void bucketsort(Array& A){ size_t numBuckets=A.size(); iarray<List> buckets(numBuckets); //put in buckets for(size_t i=0;i!=A.size();i++){ buckets[int(numBuckets*A[i])].push_back(A[i]); } ////get back from buckets

该函数定义为

void bucketsort(Array& A){
  size_t numBuckets=A.size();
  iarray<List> buckets(numBuckets);

  //put in buckets
  for(size_t i=0;i!=A.size();i++){
    buckets[int(numBuckets*A[i])].push_back(A[i]);
  }

  ////get back from buckets
  //for(size_t i=0,head=0;i!=numBuckets;i++){
  //size_t bucket_size=buckets[i].size();
  //for(size_t j=0;j!=bucket_size;j++){
  //  A[head+j] = buckets[i].front();
  //  buckets[i].pop_front();
  //}
  //head += bucket_size;
  //}
 for(size_t i=0,head=0;i!=numBuckets;i++){
   while(!buckets[i].empty()){
     A[head]          = buckets[i].back();
     buckets[i].pop_back();
     head++;
   }
 }

  //inseration sort
  insertionsort(A);
}
结果显示在下图中 其中,行从使用列表作为存储桶的列表开始,从使用向量作为存储桶的向量开始,从使用辅助数组的2开始。默认情况下,最后使用插入排序,一些使用快速排序,因为存储桶的大小很大。
注意“列表”和“列表,仅输入”、“向量,保留8”和“向量,保留2”几乎重叠。

我将尝试使用较小的大小并保留足够的内存。

链表不是数组。它们执行查找等操作的速度要慢得多。STL排序很可能有一个特定的列表版本,它考虑到了这一点并进行了优化,但是您的函数盲目地忽略了它使用的容器。您应该尝试使用STL向量作为数组。

iarray<List> buckets(numBuckets);
在某些实现中,
.size()
可以是O(n)。不太可能,但是


经过研究,我发现 解释std::_List_node_base::hook的代码


似乎只是在列表中的给定位置插入一个元素。应该不会花很多钱。

我想可能有趣的问题是,为什么要创建数量过多的桶

考虑输入
{1,2,3},numBuckets=3
。包含
bucket[int(numBuckets*A[i])的循环将展开到

buckets[3].push_back(1);  
buckets[6].push_back(2);  
buckets[9].push_back(3);  
真的吗?九桶三值

考虑是否传递了范围为1..100的置换。您将创建10000个桶,并且只使用其中的1%。。。每个未使用的bucket都需要在其中创建一个列表。。。必须迭代,然后在读出循环中丢弃


更令人兴奋的是,对列表1..70000进行排序,然后看着堆管理器爆炸式地试图创建49亿个列表。

在我看来,这里最大的瓶颈是内存管理功能(例如
new
delete

快速排序(STL可能使用优化版本)可以就地对数组进行排序,这意味着它完全不需要堆分配。这就是它在实践中表现如此出色的原因

桶排序依赖于额外的工作空间,这在理论上被认为是随时可用的(即,假设内存分配根本不需要时间)。实际上,内存分配可能需要(大的)恒定时间到所请求内存大小的线性时间(例如,在分配页面时,窗口将需要时间将页面内容归零)。这意味着标准的链表实现将受到影响,并支配您的排序的运行时间


尝试使用自定义列表实现,为大量项目预先分配内存,您应该会看到排序运行得更快。

我没有真正了解代码的细节,因为在我的研究中,我对Java了解不够,虽然我在算法和C编程方面有一些经验,但以下是我的观点:

Bucket Sort假设数组中元素的分布是公平的,这实际上更像是Bucket Sort在O(n)上工作的条件,注意在最坏的情况下,可能是在一个Bucket上放置了大量元素,因此,在下一次迭代中,您将处理与您最初尝试修复的问题几乎相同的问题,这将导致您的性能不佳

注意,Bucket sort的实际时间复杂度是O(n+k),其中k是Bucket的数量,您是否计算了Bucket?k=O(n)吗

bucket排序中最浪费时间的问题是在对bucket的分区结束后出现空bucket,当连接已排序的bucket时,如果不实际测试它,就无法判断bucket是否为空


希望我能帮忙。

O-界是渐近定义的。在现实生活中,总是需要考虑的因素。难道不应该有更少的桶吗?说
A.size()/some_const
?还是一个固定的数字(10100)?对Peter G来说:这几乎是真的,但我不这么认为。我认为大小足够大,最重要的是时间的增加不是O(n),而是O(n^1.26)。在这种情况下,我不会自己写任何排序,而是使用迄今为止看到的最快解决方案:STL的排序+1只是为了良好的图形和视觉环境:希望每个问题都能如此麻烦STL列表被用作bucket,只使用push_back front和pop_front,这应该需要常量时间。唯一的排序是insertionsort(A)其中A实际上是双类型数组。@luoq:您在列表上调用的是
size()
,它是O(N)而不是O(1)。@Oli Charlesworth:应该是O(1)。此外,他拥有的桶数与数组中的元素数一样多。如果分布是均匀的,则每个列表的长度应为~1。@Loic:O(1)
size()
与O(1)
splice()
冲突,因此可能,也可能不冲突。GNU实现就是一个O(N)时间的例子(它调用
std::distance()
@Loïc Février:查看SGI文档,它们记录了函数的复杂性。如果您查看“新成员”部分,所有版本的
splice
,包括“范围”版本都记录为“此函数是恒定时间”。要保持时间不变,就不能在该范围内进行迭代!请参阅size()在我的环境(GCC)中似乎是恒定的,我将尝试您的第一个想法即使是恒定的,从列表中获取所有元素的“正确”方法是使用空/front/pop。这不能解释这么长的时间。我只使用初始化存储桶来运行函数(只保留前两行),从n=1000到4096000,运行时间是原始值的2%-5%。好的。试试这个+“放入桶”而不是“返回”。查看哪一行是问题所在。我将尝试使用数组存储存储桶的大小,并根据大小信息将数组直接复制到新的。因此避免使用链表。然后我将检查运行时间。数组的内容在[0,1]中随机生成。因此,只需使用numBuckets桶即可
//get back from buckets
for(size_t i=0,head=0;i!=numBuckets;i++)
  while(!buckets[i].empty())
  {
    A[head++] = buckets[i].front();
    buckets[i].pop_front();
  }
buckets[3].push_back(1);  
buckets[6].push_back(2);  
buckets[9].push_back(3);