雙樞軸快排算法html
Vladimir Yaroslavskiy算法
初版:2009-02-16數組
最後更新:2009-09-11編程語言
譯者:黑狗 Email:blackdog@gmail.com性能
介紹測試
在計算機科學中數據排序是最基礎的一個問題,尤爲是數組對象是原生的,例如整型,字節,浮點等等。因爲排序方法在計算機系統和其餘數據處理系統中扮演了重要的角色,咱們對尋找比現存算法更好的算法就顯得興趣十足。咱們使用了大量的開銷最大的操做來比較排序算法。這些操做(比較和交換)的效率受排序技術的影響。快排算法是一個高效的,被普遍使用的排序過程,他須要C*n*ln(n)次操做。這裏的n指數組的大小。問題關鍵在於找到一個最小系數C的算法。這裏有許多嘗試用於改進快排算法中的古典不變式:ui
1. 從數組找找到一個元素,成爲樞軸spa
2. 從新排列元素,使小於樞軸的元素置於前,大於的置於後(相等的能夠在任何一邊)。在這樣操做之後,樞軸元素就落到了他的最終位置。指針
3. 遞歸的處理大於和小於樞軸的子數組。
Hoare,Helman,Knuth,Sedgewick以及其餘的一些科學家將大多數精力都花費在具體的「劃分和優勝」算法實現。或者是嘗試根據特定的樞軸元素的選擇來提升性能。但他們都使用了古典的劃分方案——將樞軸劃分爲兩個部分。
咱們能夠向你們展現使用兩個樞軸元素(或者說將數組劃分爲三部分)是更加高效的,特別是在大數組的狀況下。咱們推薦一種新的算法——雙樞軸快排方案。在這種狀況下,他比已知的這些實現方案更快。雙樞軸快排算法經過不一樣的輸入和原生數據類型來進行研究。和古典的快排算法,以及JDK6.0平臺上的快排實現比起來,他在交換操做上是有優點的。
新的雙樞軸快排算法
新的快排算法用兩個樞軸元素P1和P2將數組T[]a劃分爲三個部分。(所以,他須要三個指針L,K,G,和left,right——用於標記遞歸的首尾數組)這裏的T指代的是原生數據類型(例如整型,浮點,字節,字符,雙精度,長整型和短整型)。正如Figure 1所示:
這個算法執行如下的步驟:
1. 對於小數組(長度小於27),使用插入排序。
2. 選擇兩個樞軸元素P1和P2。好比咱們可使用第一個元素a[left]做爲P1,a[right]做爲P2。
3. P1必須小於P2,不然交換。所以,他被分爲了一下幾個部分
a) Part I使用使用標記left+1到L-1的元素。他們小於P1
b) Part II使用標記L到K-1的元素。他們大於等於P1,小於或等於P2
c) Part III使用標記G+1到right-1。他們大於P2
d) Part IV使用標記K到G,包含剩下的須要檢查的元素
4. Part IV中的下一個元素a[K]和數週元素P1和P2進行比較,記錄相應的part I,II和III的位置。
5. 指針L,K和G根據相應的指向而變換
6. 當K小於等於G時重複執行4-5
7. 樞軸元素P1和Part I的尾部元素交換,P2和part III的首部元素交換
8. 對於part I,II和III遞歸調用,重複執行1-7部
數學證實
雙樞軸快排的平均比較次數爲2*n*ln(n),平均交換次數爲0.8*n*ln(n)。而古典快排算法分別爲2*n*ln(n)和1*n*ln(n)。完整的數學研究已經完成,並將拷貝到此處(待定)。(坑死爹!找了個沒完成的論文!次凹!誰給我提供一個連接啊!)
比較和總況
新的雙樞軸快排算法擁有如下的優點:
l 對於原始數據類型,將數組劃分爲三部分比古典的方法更高效。和古典快排,JDK6中的快排比起來,數組越大新的算法在比較上的性能就越好。例如,咱們採用了以下的數據量進行試驗:2000000個隨機整型元素分別使用雙樞軸,古典和JDK6快排執行50次,並分析其總時間。他們分別花費16.5,,1.9和20.3秒。對於整型的雙樞軸方法能夠被簡單的調整後,完成另外一種數值,字符串和其餘可比較的類型。
l 咱們所推薦的雙樞軸方法在古典的指定採樣數組和重複元素數組的表現也更加快捷。在這些狀況下,雙樞軸快排在執行了全部的測試狀況以後,比JDK6快排快。雙樞軸耗費55,JDK6耗費100。
l 這種算法對於特殊的過程當中,經過樞軸元素P1,P2的選擇還有其餘的改進。咱們不選取a[left]和a[right]元素,而是選擇兩個中間元素。在任意數據源的狀況下,這種修正並無使雙樞軸快排的屬性更糟糕:
Int third = arrayLength / 3;
P1 = a[left + third];
P2 = a[right - third];
咱們能夠將這種算法的特徵概括以下:
l 省時
l 採用「劃分和優勝」策略
l 採用雙樞軸而不是單樞軸
l 可用於數據分析的更搞笑的排序過程
l 雙樞軸算法將會被推薦在下一個版本的JDK發佈版中。
JAVA編程語言的實現
public static void sort(int[] a){
sort(a,0,a.length);
}
public static void sort(int[] a, int fromIndex, int toIndex){
rangeCheck(a.length, fromIndex, toIndex);
dualPivotQuicksort(a, fromIndex, toIndex – 1, 3);
}
private static void rangeCheck(int length, int fromIndex, int toIndex){
if (fromIndex > toIndex) {
throw new IlleagalArgumentException(「fromIndex > toIndex」);
}
if (fromIndex < 0) {
throw new ArrayIndexOutOfBoundsException(fromIndex);
}
if (toIndex > length) {
throw new ArrayIndexOutOfBoundsException(toIndex);
}
}
private static void swap(int[] a, int I, int j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
private static void dualPivortQuicksort(int[] a, int left, int right, int div) {
int len = right – left;
if (len < 27) { // insertion sort for tiny array
for (int i = left + 1; i <= right; i++) {
for (int j = i; j > left && a[j] < a[j – 1]; j--) {
swap(a, j, j - 1);
…………….(代碼就不貼了,你們百度一下或者看JDK 7 Arrays.sort就是了,搞不懂在論文裏寫那麼全的代碼作什麼…)
實驗結果
咱們已經進行了大量的測試,這裏是執行的時間:
Server VM:
後面的就不譯了,都是引用神馬的
附上我找到的原文連接,坑死爹啊,求人幫我找官方一點的論文,我沒找到啊,又很差意思給做者email。這一篇一點都不嚴謹!比JDK6的快排論文差太遠了!