算法导论一分治法
分治法的實“力”
- 歸并排序
- 乘方問題(斐波那契數列)
- 遞歸
- 自下而上的遞歸
- 矩陣的n次冪
最近閑了點開始肝算法導論了,先介紹一些可能貫穿在整個算法導論文章的一些思想。
首先是T(n)用來表示需要的時間,我們往往研究三種情況的T(n)。第一種是最壞情況(worst),用大佬的話說就是給用戶一個保證,保證不管輸入的數據時間復雜度不會比這個更差了,比如你說這個程序至多運行三秒,這個沒有問題但如果你說是至少運行三秒,那它會不會運行三年呢,這不是一個可靠的保證。第二種平均(average),簡單來說就是期望,或者更嚴格來說是加權的,但是我們怎么知道概率是多少呢,這就需要一些假設了。第三種情況是最好(best),我常常覺得這是tricky或者bogus,因為這種情況是基本不會發生的。
接下來介紹主定理方法,這是在遞歸中我們確定時間復雜度的方法。設T(n)=aT(n/b)+f(n).
case 1:當f(n)=O(nlogba-t),則T(n)=O(nlogba)
case 2:當f(n)=O(nlogba),則T(n)=O(nlogbalogn)
歸并排序
#include <iostream> #include <vector>using namespace std;void merge(vector<int>& arr, int L, int mid, int R) {//int* help = new int(R - L + 1);std::vector<int>help(R - L + 1);int p1 = L, p2 = mid + 1, i = 0;while (p1 <= mid && p2 <= R){help[i++] = arr[p1] > arr[p2] ? arr[p2++] : arr[p1++];}while (p1 <= mid)help[i++] = arr[p1++];while (p2 <= R)help[i++] = arr[p2++];for (int i = 0; i < R - L + 1; i++){arr[L + i] = help[i];} } void MergeSort(vector<int>& arr, int L, int R) {if (L < R){int mid = L + ((R - L) >> 2); // (L+R)/2MergeSort(arr, L, mid);MergeSort(arr, mid + 1, R);merge(arr, L, mid, R);} }int main() {vector<int> arr;int n, temp;cin >> n; //輸入n個數for (int i = 0; i < n; i++){cin >> temp; //輸入數據arr.push_back(temp);}MergeSort(arr, 0, arr.size() - 1);for (int i = 0; i < arr.size(); i++)cout << arr[i] << endl;system("pause");return 0;}乘方問題(斐波那契數列)
遞歸
這是樸素算法,可以利用斐波那契數列的數學表達式進行遞歸func2(n - 1) + func2(n - 2),很顯然即便不經過數學計算,直覺告訴這個算法運行的時間非常長,是指數級別的,這里給出具體的時間復雜度O(αn)其中α=(1+5\sqrt{5}5?)/2
int func2(int n) {if (n <= 2) return 1;else returnfunc2(n - 1) + func2(n - 2); }自下而上的遞歸
上面的遞歸做了非常多無用功與重復計算,計算第n個數時第n-1個數已經計算出來了就不需要再計算了。
int func1(int n,int x,int y) {if (--n)return func1(n, y, x + y);elsereturn x + y; }矩陣的n次冪
本來還有一種方法叫平方遞歸式但是比較難用算法表示出來,所以直接來看矩陣n次冪法,這里運用了斐波那契數列的性質(Fn+1,Fn,Fn,Fn-1)=(1,1,1,0)n
而計算n次冪則完全可以用分支的思想實現快速冪,比如xn=xn/2xn/2時間復雜度就降下來了。
#include<iostream> #include<time.h> using namespace std;struct Mat {int m[2][2]; };int func1(int n,int x,int y) {if (--n)return func1(n, y, x + y);elsereturn x + y; }int func2(int n) {if (n <= 2) return 1;else returnfunc2(n - 1) + func2(n - 2); }Mat mul(Mat a, Mat b) {Mat c;c.m[0][0] = 0;c.m[0][1] = 0;c.m[1][0] = 0;c.m[1][1] = 0;for (int i = 0; i < 2; i++)for (int j = 0; j < 2; j++)for (int k = 0; k < 2; k++)c.m[i][j] += a.m[i][k] * b.m[k][j];return c; }Mat init() {Mat a;for (int i = 0; i < 2; i++) {for (int j = 0; j < 2; j++) {a.m[i][j] = 1;}}return a; }int func3(int n) {Mat res;Mat base;res.m[0][0] = 1;res.m[0][1] = 0;res.m[1][0] = 0;res.m[1][1] = 1;base.m[0][0] = 1;base.m[0][1] = 1;base.m[1][0] = 1;base.m[1][1] = 0;while (n != 0) {if (n & 1) {res = mul(res, base);}base = mul(base, base);n >>= 1;}return res.m[0][1]; }int main() {int n = 30000;clock_t start;clock_t end;//while (n > 2) {start = clock();//cout<<func1(n - 2, 0, 1)<<endl;//cout << func2(n -1) << endl;cout << func3(n-1) << endl;//Mat res = func3(n-1);//for (int i = 0; i < 2; i++)// for (int j = 0; j < 2; j++)// cout << res.m[i][j];end = clock();cout << n << " " << (double)(end - start)/CLOCKS_PER_SEC << endl;//n--;//} }總結