黑馬程式設計師____四種排序演算法的比較分析
阿新 • • 發佈:2019-01-01
下面將詳細介紹隨機數的生成以及四種排序演算法的設計技巧。
1)隨機數生成
由於題目要求生成[0,2……32−1]之間的隨機數,而c標準庫中的隨機數函式rand()只能生成[0,32767]之間的隨機數,因此採用拼接的方法來生成32位的隨機數。
將32位的數分成三段,即2位,15位,15位三段。後面兩段可以直接用rand()函式生成,前面2位的段可以去尾數之後再對4取模,這樣保證了所有數的等概率。
2)插入排序
按照書上的虛擬碼實現的,由於演算法本身已十分簡單,沒有可改進的變化。
3)歸併排序
與書上的虛擬碼相比,除去了R陣列,由於在歸併過程中,右半邊的資料沒有必要複製出來做保護,它不可能會被提前佔用位置,是安全的,因此沒有必要將右半邊的資料拷貝出來,然後在填回原陣列,並且當最後剩餘的大資料都在右半邊時,可以不用再執行拷貝。具體見如下虛擬碼。
int n1 = q - p + 1;
unsigned int *L = new unsigned int[n1];
memcpy(L, A + p, 4 * n1);
int i = 0, j = q + 1;
while (i < n1 && j <= r){
if (L[i] <= A[j]){
A[p] = L[i];
p++;
i++;
}else{
A[p] = A[j];
p++;
j++;
}
}
//如果i<n1,則證明左半邊還有資料,將其拷貝到原陣列中
for (; i < n1; i++){
A[p] = L[i];
p ++;
}
4)快速排序
程式碼實現基本按照書上提供的虛擬碼,除了一個小細節方面,書上在交換A[j]和A[q]時可以先判斷一下j和q是否一樣,如果一樣的話就沒必要自己和自己交換位置了。
5)基數排序
基數排序的穩定排序使用的是計數排序,計數排序的虛擬碼書上已經給了很詳細了。基數排序只有兩點可以研究,
a)研究劃分的每一小段應該包含多少位,即r的大小。例如r = 8時,將32位的數分成4個8位的數,這樣要做4次計數排序,每次排序的取值範圍是0到255 。r越大,次數越少,但是每次的工作量越大;r越小,次數越多,每次的工作量越小。書上給出了排序的時間複雜度公式 br (n+2r),因此書上給出了理論上的r的最合理的值為lg(n),但是可以發現b/r基本是一個小數,因此當 br 為同一個整數值的時候,r越小越好。因此理論上的最合理的值要在lg(n)的基礎上做個修正,找到相同的 br 的情況下最小的r,但是理論和實驗還是有些不同的,這個會在實驗部分做詳細分析。
b)研究如何快速的將原來的32位整數拆分為r位的若干段,這裡取模的話肯定是可以實現的,但是由於取模運算非常慢,因此採用最直接的位操作進行每段的提取,可以先將原32位數進行右移之後再與11..111進行與運算得到想要的r位,公式如下 a= A≫offset & ( 1≪ r + 1 − 1)
其中offset=i ∙r (i=0,1,2…)
四、實驗分析
測試機器環境如下:
CPU:AMD Athlon Ⅱ×4 640 @ 3.0 GHz
記憶體:4.00GB(3.25GB可用)
作業系統:win7 旗艦版 32位
編譯環境:vs2010 C++
編譯開關:vs2010 預設release開關
1)隨機數生成
由於題目要求生成[0,2……32−1]之間的隨機數,而c標準庫中的隨機數函式rand()只能生成[0,32767]之間的隨機數,因此採用拼接的方法來生成32位的隨機數。
將32位的數分成三段,即2位,15位,15位三段。後面兩段可以直接用rand()函式生成,前面2位的段可以去尾數之後再對4取模,這樣保證了所有數的等概率。
2)插入排序
按照書上的虛擬碼實現的,由於演算法本身已十分簡單,沒有可改進的變化。
3)歸併排序
與書上的虛擬碼相比,除去了R陣列,由於在歸併過程中,右半邊的資料沒有必要複製出來做保護,它不可能會被提前佔用位置,是安全的,因此沒有必要將右半邊的資料拷貝出來,然後在填回原陣列,並且當最後剩餘的大資料都在右半邊時,可以不用再執行拷貝。具體見如下虛擬碼。
int n1 = q - p + 1;
unsigned int *L = new unsigned int[n1];
memcpy(L, A + p, 4 * n1);
int i = 0, j = q + 1;
while (i < n1 && j <= r){
if (L[i] <= A[j]){
A[p] = L[i];
p++;
i++;
}else{
A[p] = A[j];
p++;
j++;
}
}
//如果i<n1,則證明左半邊還有資料,將其拷貝到原陣列中
for (; i < n1; i++){
A[p] = L[i];
p ++;
}
4)快速排序
程式碼實現基本按照書上提供的虛擬碼,除了一個小細節方面,書上在交換A[j]和A[q]時可以先判斷一下j和q是否一樣,如果一樣的話就沒必要自己和自己交換位置了。
5)基數排序
基數排序的穩定排序使用的是計數排序,計數排序的虛擬碼書上已經給了很詳細了。基數排序只有兩點可以研究,
a)研究劃分的每一小段應該包含多少位,即r的大小。例如r = 8時,將32位的數分成4個8位的數,這樣要做4次計數排序,每次排序的取值範圍是0到255 。r越大,次數越少,但是每次的工作量越大;r越小,次數越多,每次的工作量越小。書上給出了排序的時間複雜度公式 br (n+2r),因此書上給出了理論上的r的最合理的值為lg(n),但是可以發現b/r基本是一個小數,因此當 br 為同一個整數值的時候,r越小越好。因此理論上的最合理的值要在lg(n)的基礎上做個修正,找到相同的 br 的情況下最小的r,但是理論和實驗還是有些不同的,這個會在實驗部分做詳細分析。
b)研究如何快速的將原來的32位整數拆分為r位的若干段,這裡取模的話肯定是可以實現的,但是由於取模運算非常慢,因此採用最直接的位操作進行每段的提取,可以先將原32位數進行右移之後再與11..111進行與運算得到想要的r位,公式如下 a= A≫offset & ( 1≪ r + 1 − 1)
其中offset=i ∙r (i=0,1,2…)
四、實驗分析
測試機器環境如下:
CPU:AMD Athlon Ⅱ×4 640 @ 3.0 GHz
記憶體:4.00GB(3.25GB可用)
作業系統:win7 旗艦版 32位
編譯環境:vs2010 C++
編譯開關:vs2010 預設release開關