【国外文章】关于国外博主attractivechaos的基数排序radix的升级版本的代码注释

原文地址。经过做者的修改,排序速度确实很是快。是一种创建在底层优化上的排序方法。主要的作法是将排序的数字变成二进制,而且安放了256个桶。 做者也将这种算法写到了他的开源做品klib(一个c语言的标准库)中。github地址git

我业余时间一边找资料,一边翻译代码,先后大概用了半个月的时间。因此喜欢的朋友就收藏一下吧 ^_^github

一 做者的话

  • 当我写的代码的排序速度遇到瓶颈了,我参考了这篇文章,以后的速度比我原来的一个版本是约40%。比STL的std::sort快2.5倍。对大整数数组排序,基数排序才是效率杠杠的。它比其余标准算法快得多也简单的多。算法

二 代码的注释和解读

1. 代码主要部分贴图

// sort between [$beg, $end); take radix from ">>$s&((1<<$n_bits)-1)"
void rs_sort(rstype_t *beg, rstype_t *end, int n_bits, int s)
{
  rstype_t *i;
  int size = 1<<n_bits, m = size - 1;
  rsbucket_t *k, b[size], *be = b + size; // b[] keeps all the buckets
 
  for (k = b; k != be; ++k) k->b = k->e = beg;
  for (i = beg; i != end; ++i) ++b[rskey(*i)>>s&m].e; // count radix
  for (k = b + 1; k != be; ++k) // set start and end of each bucket
    k->e += (k-1)->e - beg, k->b = (k-1)->e;
  for (k = b; k != be;) { // in-place classification based on radix
    if (k->b != k->e) { // the bucket is not full
      rsbucket_t *l;
      if ((l = b + (rskey(*k->b)>>s&m)) != k) { // different bucket
        rstype_t tmp = *k->b, swap;
        do { // swap until we find an element in bucket $k
          swap = tmp; tmp = *l->b; *l->b++ = swap;
          l = b + (rskey(tmp)>>s&m);
        } while (l != k);
        *k->b++ = tmp; // push the found element to $k
      } else ++k->b; // move to the next element in the bucket
    } else ++k; // move to the next bucket
  }
  for (b->b = beg, k = b + 1; k != be; ++k) k->b = (k-1)->e; // reset k->b
  if (s) { // if $s is non-zero, we need to sort buckets
    s = s > n_bits? s - n_bits : 0;
    for (k = b; k != be; ++k)
      if (k->e - k->b > RS_MIN_SIZE) rs_sort(k->b, k->e, n_bits, s);
      else if (k->e - k->b > 1) rs_insertsort(k->b, k->e);
  }
}

经过分析代码咱们就能看出,rs_sort递归遍历直到s==0的时候为止。数组

桶排序才去的是十进制,换句话说就是放十个桶,可是做者根据cpu底层结构将数字转换成了256进制,换句话说,他一共要放256个桶。

2. 代码逐行解析注释

// sort between [$beg, $end); take radix from ">>$s&((1<<$n_bits)-1)"

1.第一行的注释, 对起始地址是$beg和$end的数组进行排序。基数选择的是当前数字右移s位 与(运算)2的n次方-1wordpress

for (k = b; k != be; ++k) k->b = k->e = beg;

2.经过循环, 将每一个桶的beg指针指向待排序数组的第一个数字的地址(即beg)优化


for (i = beg; i != end; ++i) ++b[rskey(*i)>>s&m].e;

3.做者在这里将数字变成了256进制,经过将该数字右移s位&m的方式,来获取这个数字右移s位后的值(若是右移以后发现变成了0 那么就是0),并将相对应序号的桶的储存量加一。例如 获取该数字右移s位后的值是132,那么就将132号桶的存储量加一。翻译

这么作的目的在于 采起由高向低遍历的方式,先将数字转化成256进制,而后获取这个数字的右移s位的值。并放入桶中。

用十进制举例子,这样至关于十进制中,咱们先获取万位的数字,分别将他们放在1-10的木桶中,再获取千位依次放入,而后获取百位,十位,个位。


for (k = b + 1; k != be; ++k) // set start and end of each bucket
    k->e += (k-1)->e - beg, k->b = (k-1)->e;

4.每一个木桶里面装几个都已经作好了,可是每一个木桶的起始位置都是指向的beg,如今须要将他们连起来,b[x+1]的起始指针指向b[x]的结束指针。指针


5.排序开始了啊 敲黑板!!! 。由于上一步里面只是将相对应的桶的存储量+1 尚未进行排序!!! 因此在这里就要开始排序了!!!。code

for (k = b; k != be;) { // 开始循环木桶
    if (k->b != k->e) { // 若是这个木桶不是空的,或者木桶里面的数字尚未完成遍历
      rsbucket_t *l;
      if ((l = b + (rskey(*k->b)>>s&m)) != k) { // 不一样的桶进行交换
        rstype_t tmp = *k->b, swap;
        do { // 循环 直到咱们找到这个桶里面应该存储的数字
          swap = tmp; tmp = *l->b; *l->b++ = swap;
          l = b + (rskey(tmp)>>s&m);
        } while (l != k);
        *k->b++ = tmp; // 将数字放到桶里面
      } else ++k->b; // 移动到桶里面的下一个元素
    } else ++k; // 移动到下一个桶
  }

3. 总结

模拟了一下100万条数据进行排序,发现速度确实比其余的排序速度快 大概能够达到做者说的2.5倍。平时市面上的radix排序都是用的十进制,大都是实现了算法 可是排序速度确实不够快。排序

若是还有什么问题 欢迎你们留言。

相关文章
相关标签/搜索