# Lecture 04 - Elementary Sort > 課程內容 : 中興大學資工系 范耀中教授 (112 學年度第 2 學期) > > 其他科目內容請參見 [[Here]](https://hackmd.io/@ChenZE/By4SOO6Jyl) ## 1. Selection sort ### 1.1 Selection sort demo > In iteration `i` , find index `min` of smallest remaining entry, Then Swap `a[i]` and `a[min]` :::info (1) 每次的迭代會看過整個 Array 並挑選(select)最小的元素 (2) 若 Array size $=n$ 則排序次數依序為 $n \rightarrow n-1 \rightarrow n-2 \rightarrow \cdot\cdot\cdot$ ::: * Initial : `Array, a = [7, 10, 5]` and index `i = 0` * Index `i = 0` and `a[min] = a[2] = 5` * Swap `a[0]` and `a[2]` * Output : `Array = [5, 10, 7]` * Index `i = 1` and `a[min] = a[2] = 7` * Swap `a[1]` and `a[2]` * Output : `Array = [5, 7, 10]` ### 1.2 Analysis Selection sort 使用了 $(N-1)+(N-2) +...+1+0 \sim \cfrac{N^2}{2}$ 次的比較(compare)以及 $N$ 次的搬移(exchange) 特色 : * Running time 與 input size 沒有關係 * 即使是給定已經排序好的陣列,也是需要二次方的時間複雜度 * 資料搬移的量最小(Linear cost) ## 2. Insertion sort ### 2.1 Insertion sort demo > In ireration `i` , swap `a[i]` with each larger entry to its left :::info (1) 從 Array 裡面抽牌(照 index 順序抽) (2) 抽到某張牌後往左看(已經排好的)並依序比較 (3) 比該張抽到的牌大就互換,尋找最佳的插入位置 ::: * Initial : `Array a = [7, 10, 5, 3]` * Index : `i = 0` (抽牌) * Compare : `j = 0` (比較) * Index : `i = 1` * Compare : `j = 1` * `a[j-1] = a[0] < a[j] = a[1]` , 不動 * Index : `i =2` * Compare : `j = 2` * `a[j-1] = a[1] > a[j] = a[2]` , 互換 * a = [7, 5, 10, 3]` * Compare : `j = 1` * `a[j-1] = a[0] > a[j] = a[1]` , 互換 * `a = [5, 7, 10, 3]` * Index : `i = 3` * Compare : `j = 3` * `a[j-1] = a[2] > a[j] = a[3]` , 互換 * `a = [5, 7, 3, 10]` * Compare : `j = 2` * `a[j-1] = a[1] > a[j] = a[2]` , 互換 * `a = [5, 3, 7, 10]` * Compare : `j = 1` * `a[j-1] = a[0] > a[j] = a[1]` , 互換 * `a = [3, 5, 7, 10]` 實作上程式碼中會有兩個迴圈,外層遞增迴圈,用來依序牌;內層遞減迴圈,用來依序比較 ### 2.2 Analysis **Best case** 針對本身已經排序(遞增)的陣列,從右側未排序的元素(`a[i]`)依序抽牌時,僅需要與其左邊的元素(`a[i-1]`)比較(compare)一次即可,且不用做任何互換(exchange),總共只需要比較 $N-1$ 次,複雜度為 $N$ **Worst case** 對遞減排序的陣列,第$N$牌需要比較$N-1$次以及搬運$N-1$次,所以比較次數為 : (搬運同理) $$ N+(N-1)+...+1= \frac{(N+1) \cdot N}{2} = \frac{1}{2}N^2 $$ **Average case** 直接取 Best case 與 Worse case 的平均 : $$ (N + \frac{1}{2} N^2) \cdot \frac{1}{2} \sim \frac{1}{4} N^2 $$ ## 3. Comparable interface 自己寫的物件,系統預設排序不見得能夠使用,通常只有 `string`、`int`等內建的 data type 可以使用 system sort > **Question :** 如何將自定義的物件用在 system sort ? > **Answer :** 需要定義兩個物件如何比大小 ### 3.1 Callback > **Definition.** Callback > Call = reference a executable code (引用一個可執行的程式) Callback function 是一種在特定事件或條件發生時才會被呼叫的函式,幾乎所有的程式語言都有支援。可以在指定的時候再呼叫這個函式,且這個函式會被當作參數來傳遞到另一個函數中,如同字面上的名稱 call back(待會叫你) 以排序舉例,當使用系統排序針對自定義物件作排序時,需要先創建一個函式 `comparable()` 來定義如何排序,這個函式就是 callback function (需要排序時才會被呼叫)。當使用系統排序時,系統排序函式又會呼叫 `comparable()` 函式來做比較 其他程式語言的 callback function 支援方式 * C : function pointers * Python : first-class function(同等函式) ![EC0A72C9-33DB-44F1-8609-DF9F889C3ACD](https://hackmd.io/_uploads/S1XGIFGGA.jpg) ## 4. Shell sort **Insertion sort 的優點** * 適合用在較小的資料 * 適用於已經有部分排序好的數據 * Ex : 對排序好的遞增數列(best case),其複雜度為 $N$ > **Core concept of shell sort** > 1. 利用 insertion sort 的優點 > 2. 創造一個可以使用 insertion sort 的環境 ### 4.1 Shell sort demo * 選擇一個適當的 $h$ 值 * 兩種理解方式 * 看法(1) * 每 $h$ 個元素挑出來形成一個小的 Array * 針對這個小的 Array 做 insertion sort * 排序完後再使用類似 sliding window 的方式依照 $h$ 值繼續挑選小的 Array 做 insertion sort (下圖紅色與藍色敘述) * 看法(2) * 從頭開始第 $i$ 個元素與第 $i+h$ 個元素做比較 * 若 `a[i] > a[i+h]` 則互換(下圖黑色圖示) * 排序完的 Array 稱為 h-sorted * 往下挑選較小的 $h$ 值繼續排序 * 當 $h=1$ 時就是傳統的 insertion sort ![27D81EF8-4648-4E86-A76A-2A3341124E28](https://hackmd.io/_uploads/B1t09KGG0.jpg) **shell sort 的精神** 1. 當 $h$ 值較小時,基本上已經快排好,接近線性成本 2. 當 $h$ 值較大時,sub-array 較小,可以使用 insertion sort 排序 :::info (1) 最佳 $h$ 值目前尚未定論 (2) 如果 $h$ 值選擇 : $1$、$2$、$4$、$8$、...則容易找到重複的狀況,例如 8-sorted與4-sorted會重複做 ::: ### 4.2 Analysis > Shell sort 的 worst-case 是 $N^{1.5}$ ![S__2162692](https://hackmd.io/_uploads/Sy-nLbsQ0.jpg) 由上表可知 * 適當的調整 $h$ 值是可以做到 $N^{1.3}$ 等級的效能 * 當 N 值不是很大(Ex: $N=20,000$)時,Shell sort 是佔有優勢的($N^{1.3}=390\text{k}$) * 但當 N 值很大(Ex: $N=80,000$)時,shell sort 不見得是最好的($N^{1.3}=2366 \text{k}$) ==當 N 值不是很大時,Shell sort 是個好選擇== 實務上,Shell sort 常被用在許多的**嵌入式系統**中 ### 4.3 Summary ![S__2162694](https://hackmd.io/_uploads/HyH9tZo70.jpg) h-sequence 的動作可視為 $\log N$ 規模的等級,每個子陣列 h 都要做 n 次,所以 Best case 是 $N \log N$ ## 5. Shuffling ### 5.1 How to shuffle an array > **Goal :** > Rearrange array so that result is a uniformly random permutation :::info 雖然是做 shuffle(洗牌),但本質上還是做一種排序。做法是**隨機**產生一組數字作為排序的**順序**,且元素**排序在每個位置的機率都相同** ::: ### 5.2 Knuth shuffle > * In iteration $i$ , pick integer $r$ between $0$ and $i$ uniformly at random > * Swap $a[i]$ and $a[r]$ ![S__2179074](https://hackmd.io/_uploads/HJG6tH44C.jpg) 洗牌過程中,必須從頭到尾把整個 Array 看過一次,所以**複雜度 $=N$** **Prove : probability is evenly** ![S__2179075](https://hackmd.io/_uploads/B1-95BE4R.jpg) :::danger **問題:question:** 實作上在 randomly pick 階段要如何進行? :::