# 基礎演算法 ## 遞迴 指函數本身使用到自己 ::: danger 函數必須有盡頭,要有終止條件 ::: ### 階乘 因為f(a) = a\*f(a-1),例如f(3) = 3\*f(2) = 3\*2\*f(1) = 3\*2\*1 ```cpp int f(int in){ if(in <= 0 ){ return 1; } else if(in > 0 ){ return in*f(in-1); } } ``` ### 次方 因為f(m, n)=m\*f(m, n-1),例如:f(2, 3) = 2\*f(2, 2) = 2\*2\*f(2, 1) = 2\*2\*2\*f(2, 0) = 2\*2\*2\*1 ```cpp int f(int m, int n){ if(n <= 0 ){ return 1; } else if(n > 0 ){ return m*f(m, n-1); } } ``` ## 排序 將陣列中的數字由小排到大的幾種方式,其中快速排序是以下中最快的 ### 選擇排序 建立兩個陣列,將輸入陣列中找到最小的方入另一樣陣列的最底端,持續直到一邊鎮列為空 ```cpp #include<iostream> using namespace std; int main() { int num[100]; int N; int i, j, tmp; //[input] cin >> N; for( i=0 ; i<N ; i=i+1 ) { cin >> num[i]; } //[sort] for( i=0 ; i<N ; i=i+1 ) { for( j=i+1 ; j<N ; j=j+1 ) { if( num[i] > num[j] ) { //變數交換 tmp = num[i]; num[i] = num[j]; num[j] = tmp; } } } //[output] for( i=0 ; i<N ; i=i+1 ) { cout << num[i] << " "; } cout << endl; return 0; } ``` ### 泡沫排序 從第一項開始比較,若比第二項大,則交換位子,每一次執行完整個陣列再次重複並且少檢查一項,總共檢查n!次,效率是O(n²) ```cpp for(i=size-1; i>=1; i--) //當i=1時,j只做0和1。 for(j=0; j<i; j++) if(arr[j] > arr[j+1]) //switch ``` ### 插入排序 設第一項為已排序區,其餘為未排序區。比較未排序區的第一個和已排序區,此項依序與已排序區的項比較,若比已排序區大,則跟他交換,或無,則放入已排序區(已排序區長度增加1),直到未排序區為空。兩排序區總何必為整個陣列的數量,因此不需要建立兩個陣列 ```cpp for(i=1; i<size; i++){ insert = arr[i]; j=i-1; while(j>=0 && arr[j]>insert){ arr[j+1] = arr[j]; j--; } arr[j+1] = insert; } ``` ### 合併排序 將陣列分割成兩半,若是偶數,兩半等長,若是不為1的基數,則分割成(n/2)+1和n/2,不斷分割直到剩下的陣列只有一項。比較兩個原本是同陣列的陣列,從左到右,大的放右邊,小的放左邊。從最根部的合併直到最後只剩一個陣列 影片:https://www.youtube.com/watch?v=JSceec-wEyw ### 快速排序 設立三個指標,左邊指標、右邊指標、標準指標。 0.左邊和標準指標初始都在最左邊,又邊指標則是在最右邊。 ``` [2, 5, 1, 4, 3] 2是左邊指標也是標準指標 3是右邊標準 ``` 1.從右邊指標開始移動,若右邊指標比標準指標的項還小(<=),右邊指標向左一格,直到找到此項目。 ``` [2, 5, 1, 4, 3] 2是左邊指標也是標準指標 1是右邊標準 ``` 2.接著左邊指標開始移動,若左邊指標比右邊指標的項還大(>),做邊指標往右一格,直到找到此項目。 ``` [2, 5, 1, 4, 3] 2是標準指標 5是左邊指標 1是右邊標準 ``` 3.此時若左邊指標項目和右邊指標項目不是同一項,則左右指標項目交換,接著執行上述步驟。 ``` [2, 1, 5, 4, 3] 2是標準指標 1是左邊指標 5是右邊標準 ``` 重複執行一次 ``` [2, 1, 5, 4, 3] 2是標準指標 1是左邊指標 1是右邊標準 ``` 4.若左邊指標碰觸到右邊指標,則這個項目和標準指標交換位置 ``` [1, 2, 5, 4, 3] ``` 這樣的步驟讓所有在標準指標項的左邊都小於他,在標準指標右邊的都大於他 接著排除標準指標,將兩邊的項目分割成兩個陣列,重複執行上述步驟,直到都只剩下一個項目,再依序拼接 ``` [ 2 ] [1] [ 5, 4, 3] ``` 1不用排序,543需要 ``` [ 2 ] [1] [ 3, 4, 5] ``` 排完之後5是標準指標被排除 ``` [ 2 ] [1] [ 5] [ 3, 4] ``` 34還是需要排序 ``` [ 2 ] [1] [ 5] [ 3 ] [ 4] ``` 這樣就全部分割完了,再依序拼接 ``` [ 2 ] [1] [ 5] [ 3, 4] ``` ``` [ 2 ] [1] [ 3, 4, 5] ``` ``` [1, 2, 3, 4, 5] ``` 影片:https://www.youtube.com/watch?v=AsQW27DT82I ## 輾轉相除法 這是一種求最大公因數和最小公倍數相當快的方式 分別有大數A和小數B,先把大數當被除數,小數當除數 「被除數A/除數B=整數C...餘數m」 接著把除數B變成被除數,再把餘數m變成被除數 「被除數B/除數r=整數D...餘數n」 以此類推直到被除數可以整除除數,此時除數極為最大公因數 ### 最大公因數 以上已經把最大公因數取得方法解釋完,舉個例(703, 407),基本上我們只會用到除數和被除數和餘數,所以先把整數去掉 703%407=296 407%296=111 296%111=74 111%74=37 74%37=0 因此37是703和407的最大公因數 在計算的時候會寫 ``` 1|703|407|1 |407|296| 2|296|111|1 |222| 74| 2| 74| 37| | 74| | | 0| ``` ### 最大公倍數 這算法很簡單,找出最大公因數後,把兩數除與最大公因數 再把最大公因數乘整除後的兩個數字,拿703和407舉例 703/37=19 407/37=11 37\*19\*11=7733 7733就是最大公被數 ------------------------- ## 資料來源 排序1:https://www.csie.ntu.edu.tw/~b98902112/cpp_and_algo/cpp02/selection_sort.html 排序2:https://andyli.tw/sort/#%e6%8f%92%e5%85%a5%e6%8e%92%e5%ba%8f-insertion-sort