二分查找的二分法和黃金分割點比較


筆記和代碼的思路來源:

好大學慕課浙江大學陳越、何欽銘的《數據結構》

討論3.1 黃金分割查找?

老師參與

在二分查找中,我們是取mid等於left和right的中間值,即用等分的方法進行查找。

那為什么一定要等分吶?能不能進行“黃金分割”?也就是mid=left+0.618(right-left),當然mid要取整數。如果這樣查找,時間復雜性是多少?也許你還可以編程做個試驗,比較一下二分法和“黃金分割”法的執行效率。

 
證明來自好白的小白。

二分法每次能有100%的概率能只剩50%的數據,每次剩下的期望為50%,即每次除以2。所以時間復雜度是

而黃金分割的話每次都有0.618的概率剩0.618,0.382的概率剩0.382,每次剩下的期望為0.528,即每次除以1.894。所以時間復雜度是

 

雖然兩者都是O(logN)類,但是系數不同,黃金分割法所需時間約為二分法的1.085倍(此處沒考慮取整……)。

 

假設分割點距左側的距離除以全長等於p,那么每次剩下的期望為個全長。要讓這個期望最小,我們知道p要等於1-p。所以p=0.5。所以二分法分在正中。

 
下面是是自己的代碼和測試過程:
 
  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<time.h>
  4 #include<math.h>
  5 
  6 #define MAXSIZE 1000000
  7 
  8 typedef int dataType;
  9 
 10 typedef struct node{
 11     dataType data[MAXSIZE];
 12     int length;
 13 }lis,*pList;
 14 
 15 
 16 pList createEmptyList(){
 17     pList list = (pList)malloc(sizeof(lis));
 18     if(list){
 19         list->length=-1;
 20     }
 21     return list;
 22 }
 23 
 24 int isFull(pList list){
 25     return (list->length==MAXSIZE-1);
 26 }
 27 
 28 int isEmpty(pList list){
 29     return (list->length==-1);
 30 }
 31 
 32 void insert(pList list,dataType element){
 33     if(isFull(list)){
 34         printf("the list has full");
 35         return;
 36     }
 37     list->data[++(list->length)]=element;
 38 
 39 }
 40 
 41 dataType findIndex(pList list,int index){
 42     if(isEmpty(list)){
 43         printf("the list is empty");
 44         return;
 45     }
 46     return list->data[index];
 47 }
 48 
 49 void toString(pList list){
 50     int length=0;
 51     printf("\ntoString:");
 52     while(length<=list->length){
 53         printf("%d ",list->data[length]);
 54         length++;
 55     }
 56 }
 57 
 58 
 59 /*
 60 不使用哨兵是算法就會導致每次循環進行一次i<=list->length的判斷,
 61 為了演示方便,我們這里的數組角標從1開始,0角標存放的是數組的長度+1
 62 */
 63 int orderSearch(pList list,int element){
 64     int i;
 65     for(i=0;i<=list->length && list->data[i]!=element;i++);
 66     if(i>list->length){
 67         return -1;
 68     }else{
 69         return i;
 70     }
 71 }
 72 
 73 /*
 74 使用順序查找,使用哨兵算法,
 75 如果使用哨兵算法,那么數組的第一個元素就只能存放數組的長度
 76 數組的真正的角標是從1開始的。0號角標存放的需要查找的元素element,
 77 當循環退出是,要么是找到了,返回找到元素的角標i
 78 要么是i=0;沒找到
 79 */
 80 int orderSearch2(pList list,int element){
 81     int i;
 82     list->data[0]=element;
 83     for(i=list->length;list->data[i]!=element;i--);
 84     return i;//0表示沒有找到
 85 }
 86 
 87 /*二分查找,去終點作為分割點*/
 88 int binarySearch(pList list,int element){
 89     int left=1;
 90     int right=list->length;
 91     while(left<=right){
 92         int mid=(right+left)/2;
 93         if(element>list->data[mid]){
 94             left=mid+1;
 95         }else if(element<list->data[mid]){
 96             right = mid - 1;
 97         }else{
 98             return mid;
 99         }
100     }
101     return -1;//沒找到,返回-1
102 }
103 
104 /*
105 二分查找,取黃金分割點作為中點
106 */
107 int binarySearch2(pList list,int element){
108     int left=1;
109     int right=list->length;
110     while(left<=right){
111         int mid=left+0.618*(right-left);
112         if(element>list->data[mid]){
113             left=mid+1;
114         }else if(element<list->data[mid]){
115             right = mid - 1;
116         }else{
117             return mid;
118         }
119     }
120     return -1;//沒找到,返回-1
121 }
122 
123 
124 void main(){
125     int i=0,j=0;
126     clock_t start,end;
127     double duration;//used to stored top - end
128 
129     int count =10;
130     pList list = createEmptyList();
131 
132     insert(list,MAXSIZE);
133     for(i=1;i<MAXSIZE;i++){
134         insert(list,i);
135     }
136 
137 
138 
139     start=clock();
140     start=clock();
141     for(j=0;j<count;j++){
142         for(i=0;i<MAXSIZE;i++){
143             binarySearch(list,i);//使用中點分割
144         }
145     }
146     end=clock();
147     duration=((double)(end-start))/CLK_TCK/MAXSIZE/count;
148     printf("use middle as the split point:%f\n",duration);
149 
150 
151     start=clock();
152     for(j=0;j<count;j++){
153         for(i=0;i<MAXSIZE;i++){
154             binarySearch2(list,i);//使用黃金分割點分割
155         }
156     }
157     end=clock();
158     duration=((double)(end-start))/CLK_TCK/MAXSIZE/count;
159     printf("use huangJinFenGeDian as the split point:%f\n",duration);
160 
161 
162     //toString(list);
163 
164 /*
165     insert(list,10);
166     for(i=1;i<=10;i++){
167         insert(list,i);
168     }
169     printf("%d ",orderSearch2(list,7));
170     printf("%d ",orderSearch2(list,11));
171     printf("%d ",binarySearch(list,9));
172     printf("%d ",binarySearch(list,11));
173     toString(list);
174 */
175 }
View Code

 

進行累計計時就求平均的思想,但是當count=100時,PC機運算時間太慢了,就沒有測試。

下面是count=10的測試結果,發現基本上查不了多少。但是上面的邏輯證明更加准確。

 

 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM