Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/163.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 最高有效位基数排序如何比最低有效位基数排序更有效?_C++_Algorithm_Radix Sort_Radix - Fatal编程技术网

C++ 最高有效位基数排序如何比最低有效位基数排序更有效?

C++ 最高有效位基数排序如何比最低有效位基数排序更有效?,c++,algorithm,radix-sort,radix,C++,Algorithm,Radix Sort,Radix,我刚才在看下面的问题: 被接受的答案的作者认为MSD基数排序确实更快。然而,我不明白为什么 我已经实现了LSD和MSD(基于二进制的移位操作),LSD是迭代的,只需要一个bucket数组,而MSD是递归的,每次递归调用都需要一个bucket数组 如果创建一个1000万整数的随机数组,我看不出MSD比LSD快多少,因为每次重新进入函数时都会分配额外的bucket数组,而且还必须面对递归调用本身的开销 我可以看到MSD和LSD的组合如何带来全面的提升(对前几个位运行MSD,对其余位运行LSD,以减

我刚才在看下面的问题:

被接受的答案的作者认为MSD基数排序确实更快。然而,我不明白为什么

我已经实现了LSD和MSD(基于二进制的移位操作),LSD是迭代的,只需要一个bucket数组,而MSD是递归的,每次递归调用都需要一个bucket数组

如果创建一个1000万整数的随机数组,我看不出MSD比LSD快多少,因为每次重新进入函数时都会分配额外的bucket数组,而且还必须面对递归调用本身的开销


我可以看到MSD和LSD的组合如何带来全面的提升(对前几个位运行MSD,对其余位运行LSD,以减少缓存未命中),但是考虑到MSD的递归性质以及每个递归调用都需要一个新的bucket数组这一事实,单是MSD如何比LSD更有效,与LSD不同,LSD是迭代的,并且整个排序过程只需要一个bucket数组。

您提到的问题是仅对单个位执行排序。因此,每个递归只将数组分成两个组。因此,在递归时实际上不需要存储太多内容

你下降的小组越小——越大的小组,你就放在一个堆栈上,以便以后执行。在最坏的情况下,堆栈中最多有
w
元素,其中
w
是数据类型的宽度(以位为单位)

现在,已经证明了递归在这种情况下并没有那么糟糕,为什么MSD有机会运行得更快(即缓存局部性)是站得住脚的。

答案 MSD基数中的迭代次数取决于输入大小,而LSD基数排序中的迭代次数取决于密钥长度。这通常会导致MSD基数排序比LSD基数排序需要更少的迭代次数,因此速度更快

内存分配不是问题,因为MSD基数排序可以很容易地就地实现

根本原因 我已经为LSD和MSD基数排序做了一个实现,所以我可以看到它们有哪些属性使得MSD基数排序比LSD基数排序更快

我将它们的速度与100.000.000个随机正63位整数的数组上的std::sort进行了比较(我使用了std::sort的结果,我还用于验证排序的数组),得到了以下结果:

  • 纯LSD排序:10.5s
  • 标准:排序:9.5s
  • 纯MSD排序:9.3s
  • MSD排序+插入排序:7.6s
因此,它比std::sort稍微快一点,如果使用插入排序对叶子进行排序,它会快一点

为什么MSD基数排序比LSD基数排序快?
  • 还有缓存位置,尽管我怀疑这是否真的很重要,因为LSD基数排序也会扫描整个数组,而不是执行随机访问
  • MSD基数排序可以实现为其空间复杂度为O(dk),因此仅取决于基数d和项k的长度。这可以在几乎免费的堆栈上分配。因此,它基本上是一种就地排序算法
  • 底层可以修剪。也就是说,当一个bucket只包含1个元素时,它已经被排序,因此不需要在该bucket上递归。因此,MSD基数排序只需要执行大约log(n)/log(d)次迭代。而LSD基数排序始终必须执行k次迭代
我相信这最后一点就是MSD基数排序通常比LSD基数排序快的原因。如果输入数据是均匀随机分布的,那么预期的运行时间是O(n log(n)/log(d)),而LSD基数排序的运行时间是O(n k)。通常n比k^d小很多。只有当n=o(k^d)时,LSD基数排序才会更快。但是,在这种情况下,也可以使用计数排序(k=1的基数排序)

实现
inline void插入\u排序(int64\u t*数组,intn){
对于(int i=1;i0&&array[j-1]>val){
数组[j]=数组[j-1];
j--;
}
数组[j]=val;
}
}
无效msd_排序(int64_t*数组,int n,int64_t位=60){
常数int64_t mask=int64_C(7);
//计算桶大小
整数计数[9]={};
对于(int i=0;i>bit)和掩码)+1]++;
}
//打洞。
int loc[8];
int64_t未排序[8];
int live=0;
对于(int i=0;ibit)和掩码;
数组[loc[d]]=val;
loc[d]++;
未排序的[live]=数组[loc[d]];
如果(loc[d]==计数[d+1]){
活着--;
}
}
如果(位>0){
对于(inti=0;i20){//如果替换为n>1,则不需要插入排序。
msd_排序(数组+计数[i],n,第3位);
}否则{
插入排序(数组+计数[i],n);
}
}
}
}
无效lsd_排序(int64_t*数组,int n){
常数int64_t mask=int64_C(7);
std::向量缓冲区(n);
对于(int64_t位=0;位)&掩码)+1]+;
}
//初始化写入程序位置
对于(int i=0;ibit)和掩码;
数组[count[d]]=val;
计数[d]++;
}
}
}

您可以重构代码以避免这些不必要的分配和取消分配。请注意,哪一个更快是高度依赖于实现的;我可以实现这两种方法,以便MSD排序更快,或者LSD排序更快。既然在每次递归调用中都需要bucket数组来查找稍后将递归到的组,那么如何避免重新分配呢?在这两种情况下,使用全局bucket数组或通过引用下一个递归调用发送副本都会改变您的bucket数组,从而使您无法在上一个递归调用中找到下一个组。每个级别只需要一个bucket数组。这是对数多;没有理由为每个通话分配和释放一个。这是真的。。。。。。。。。。。现在,我用一个桶来装每一个re
inline void insertion_sort(int64_t * array, int n) {
  for (int i=1; i<n; i++) {
    int64_t val = array[i];
    int j = i;
    while (j>0 && array[j-1] > val) {
      array[j] = array[j-1];
      j--;
    }
    array[j] = val;
  }
}

void msd_sort(int64_t * array, int n, int64_t bit=60) {
  const int64_t mask = INT64_C(7);
  // Count bucket sizes
  int count[9]={};
  for (int i=0; i<n; i++) {
    count[((array[i]>>bit) & mask)+1]++;
  }
  // Create holes.
  int loc[8];
  int64_t unsorted[8];
  int live = 0;
  for (int i=0; i<8; i++) {
    loc[i] = count[i];
    count[i+1]+=count[i];
    unsorted[live] = array[loc[i]];
    if (loc[i] < count[i+1]) {
      live++;
    }
  }
  live--;
  // Perform sort
  for (int i=0; i<n; i++) {
    int64_t val = unsorted[live];
    int64_t d = (val>>bit) & mask;
    array[loc[d]] = val;
    loc[d]++;
    unsorted[live] = array[loc[d]];
    if (loc[d] == count[d+1]) {
      live--;
    }
  }
  if (bit>0) {
    for (int i=0; i<8; i++) {
      n = count[i+1] - count[i];
      if (n > 20) { // If replaced by n > 1, insertion_sort is not needed.
        msd_sort(array + count[i], n, bit-3);
      } else {
        insertion_sort(array + count[i], n);
      }
    }
  }
}

void lsd_sort(int64_t * array, int n) {
  const int64_t mask = INT64_C(7);
  std::vector<int64_t> buffer(n);
  for (int64_t bit=0; bit<63; bit+=3) {
    // Copy and count
    int count[9]={};
    for (int i=0; i<n; i++) {
      buffer[i] = array[i];
      count[((array[i]>>bit) & mask) + 1]++;
    }
    // Init writer positions
    for (int i=0; i<8; i++) {
      count[i+1]+=count[i];
    }
    // Perform sort
    for (int i=0; i<n; i++) {
      int64_t val = buffer[i];
      int64_t d = (val>>bit) & mask;
      array[count[d]] = val;
      count[d]++;
    }
  }
}