实验二:线性时间选择
實驗二:線性時間選擇
?
(1)線性時間選擇問題
給定線性序集中n個元素和一個整數k,1<=k<=n.要求找出這n個元素中第k小的元素,即如果將這個n個元素依其線性序排列時,排在第k個位置的元素就是要找的元素,當k==1時,要找的就是最小的元素;當k==n,就是最大的元素;當k=(n+1)/2,稱為中位數。
?
(1)掌握線性時間選擇算法
(2)體會線性時間選擇算法中蘊含的分治思想
分治算法的基本思想是將一個規模為N的問題分解為K個規模較小的子問題,這些子問題相互獨立且與原問題性質相同。求出子問題的解,就可得到原問題的解。即一種分目標完成程序算法,簡單問題可用二分法完成。
(3)線性時間選擇
一個很簡單的想法就是先排序后找,但這樣算法的性能依賴于所選擇排序算法的性能,比如快排就很快,冒泡就很慢。
我們可以模仿快速排序算法,對輸入數組進行遞歸劃分。與快速排序不同的是,它只對劃分出的子數組之一進行遞歸處理。
一種改進的思路是:如果能在線性時間內找到一個劃分基準,使得按這個基準所劃分出的兩個子數組長度都至少為原數組長度的e倍(0<e<1是某個正常數),那么在最壞情況下用O(n)時間就可以完成選擇任務。例如,若e=9/10,算法遞歸調用所產生的子數組的長度至少縮短1/10。所以,在最壞情況下,算法所需的計算時間Tn滿足遞歸式Tn<=T(9n/10)+O(n)。由此可得T(n)=O(n)
(1)將n個輸入元素劃分成n/5個組,每組5個元素,除可能有一個組不是5個元素外。用任意一種排序算法,將每組中的元素排好序,并取出每組的中位數,共n/5個。
(2)遞歸調用Select找出這n/5個元素的中位數。如果n/5是偶數,就找它的兩個中位數中較大的一個。然后以這個元素作為劃分基準。
(所有的n/5都向上取整,取整號打不出來。。。)
?
4.1 線性時間選擇
在前面快速排序的基礎上進行線性時間選擇。
首先是先排序后找target值。這種辦法對于數組規模較小的情況有效。但對于大規模的數組,顯然有更好的辦法優化線性時間選擇的算法。
整個c文件分為5個函數:
1. swap交換函數。C語言沒有原生的swap交換函數,因此自己寫了一個。
2. Partition分區函數。用于對給定數組進行分區。
3. random函數。用于生成一定范圍內的隨機數。利用c語言原生的rand函數生成隨機數,srand函數配合time.h的time函數播種種子。
4. Quicksort函數。上個文件的。
5. Select函數。用于選擇和排序。
?
這幾個函數有一部分是從上個快速排序函數繼承下來的。
?
5.1 線性時間選擇
首先,線性時間選擇存在越界可能性,即選擇的target值超過數組本身范圍,會打印出內存中的隨機數。(c語言)截圖如下:
除去越界問題,對線性時間選擇的性能測試如下:
選擇target=4(隨便想的),在10,100,1 000,10 000,100 000順序數組查找的時間如下(忽略生成時間和打印時間):
| 數字個數 | 時間 |
| 10 | 0.385 |
| 100 | 0.347 |
| 1000 | 0.350 |
| 10000 | 0.450 |
| 100000 | 0.480 |
圖表體現的不是那么明顯,一方面是c語言沒有直接測量時間的函數,只能依賴于編譯器給出的時間,但這個時間并不是絕對精確的;另一方面,時間包含了生成隨機數和打印的時間,雖然最終的結果應該隨著x坐標是線性的,但體現不明顯。
?
查閱資料得知,c語言有時間測量模塊time_t。我決定用time_t改良時間測量的精確性。同時將數據改為線性數據。改進后重新實驗:
| 數據 | 時間(精確,ms) |
| 10 000 | 0 |
| 20 000 | 1.2 |
| 30 000 | 2.0 |
| 40 000 | 2.4 |
| 50 000 | 4.0 |
| 60 000 | 4.6 |
| 70 000 | 5.4 |
| 80 000 | 6.4 |
| 90 000 | 6.0 |
| 100 000 | 6.2 |
這次的線性就體現得很明顯了。
線性時間選擇的優化來自快速排序對基準數的優化,從自選數(一般為第一位),到隨機數,最后是中位數。
| 算法 | 快排 | 隨機選擇 | 線性時間選擇 |
| 時間復雜度 | O(nlog(n))O(nlog(n)) | O(n)??O(n2)O(n)??O(n2) | O(n)O(n) |
| 基準值 | a[p] | random | 中位數 |
?
7.1線性時間選擇
#include<stdio.h> #include<stdlib.h> #include<time.h> #include<unistd.h>// Swap the district void Swap(int a,int b){int temp=a;a=b;b=temp; }int Partition(int nums[],int left,int right,int x){int i=left,j=right+1;//int x=nums[left];while (1){while (nums[++i]<x&&i<right);while (nums[--j]>x);if(i>=j)break;Swap(nums[i],nums[j]);}nums[left]=nums[j];nums[j]=x;return j; }int random(int max,int min){int origin = rand();int random_num = origin%(max-min+1)+min;return random_num; }// int RandomizedPartition(int a[],int p,int r){ // srand((unsigned int)time(NULL)); // int i=random(p,r); // Swap(a[i],a[p]); // return Partition(a,p,r); // }int QuickSort(int nums[],int left,int right){//@param: nums[]: numbers//@param: left: the number of the [0]if(left<right){int i=left;int j=right;int temp_middle=nums[left];//standard numberwhile (i<j){//from right to left,find a number smaller than standard numberwhile (i<j&&nums[j]>=temp_middle){j--;}if(i<j){nums[i]=nums[j];i++;}while (i<j&&nums[i]<temp_middle){i++;}if(i<j){nums[j]=nums[i];j--;} }nums[i]=temp_middle;QuickSort(nums,left,i-1);QuickSort(nums,j+1,right); } }int Select(int nums[],int left,int right,int target){//judge by 75if(right-left<75){QuickSort(nums,left,right);return nums[left+target-1];}//NOT 75for(int i=0;i<=(right-left-4)/5;i++){//right-left-4 是一個劃分組號的公式。比如組內有34個元素,//i的值就是(33-0-4)/5=29/5=5//找中位數的中位數,r-p-4即上面所說的n-5int s=left+5*i,t=s+4;for(int j=0; j<3; j++) //冒泡排序,從后開始排,結果使得后三個數是排好順序的(遞增){for(int n=s; n<t-j; n++){if(nums[n]>nums[n+1])Swap(nums[n],nums[n-1]);}}Swap(nums[left+1],nums[s+2]);}int x=Select(nums,left,left+(right-left-4)/5,(right-left-4)/10);int i=Partition(nums,left,right,x),j=i-left+1;if(target<=j){// printf("target<=j,j=%d\n",j);// printf("##left=%d\n",left);// printf("##right=%d\n",right);return Select(nums,left,i,target);}else{// printf("target>j,j=%d\n",j);// printf("##left=%d\n",left);// printf("##right=%d\n",right);return Select(nums,i+1,right,target-j);}}int main(void){int target=4;//int nums[100]={3,1,7,6,5,9,8,2,0,4,13,11,17,16,15,19,18,12,10,14,23,21,27,26,25,29,28,22,20,24,33,31,37,36,35,39,38,32,30,34,43,41,47,46,45,49,48,42,40,44,53,51,57,56,55,59,58,52,50,54,63,61,67,66,65,69,68,62,60,64,73,71,77,76,75,79,78,72,70,74,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100};int max_num=100000;int nums[max_num];for(int i=0;i<max_num;i++){ nums[i]=i+1; // printf("%2d,",nums[i]);// if(nums[i]%10==0) printf("\n");}//計時clock_t stime = clock();if(target>max_num||target<=0){printf("越界了!");printf("越界結果:第%d大的數是%d\n",target,Select(nums,0,max_num,target));}else{printf("第%d大的數是%d\n",target,Select(nums,0,max_num,target));}clock_t etime = clock();printf("time is %d ms",etime-stime);return 0; }?
?
總結
以上是生活随笔為你收集整理的实验二:线性时间选择的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 前端学习(3017):vue+eleme
- 下一篇: 国内外公有云对比:功能介绍、性能测试