【國外文章】關於國外博主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排序都是用的十進制,大都是實現了算法 可是排序速度確實不夠快。排序

若是還有什麼問題 歡迎你們留言。

相關文章
相關標籤/搜索