轉載請注明:http://www.cnblogs.com/ECJTUACM-873284962/
今晚是我們學長第二次講課,講了一個三分!認真聽了一下,感覺不是很難,可能會比二分還簡單些!我就把上課講的內容歸納為一篇文章概述吧!以后也會重點講解的!
簡單點說二分是查找區間,相當於一次函數,三分就是二次函數了,求它的極值,怎么做,數學常用的是求導,計算機就用查找咯,那么請看下面的簡單概述吧!
一. 概念
在二分查找的基礎上,在右區間(或左區間)再進行一次二分,這樣的查找算法稱為三分查找,也就是三分法。
三分查找通常用來迅速確定最值。
二分查找所面向的搜索序列的要求是:具有單調性(不一定嚴格單調);沒有單調性的序列不是使用二分查找。
與二分查找不同的是,三分法所面向的搜索序列的要求是:序列為一個凸性函數。通俗來講,就是該序列必須有一個最大值(或最小值),在最大值(最小值)的左側序列,必須滿足不嚴格單調遞增(遞減),右側序列必須滿足不嚴格單調遞減(遞增)。如下圖,表示一個有最大值的凸性函數:

二、算法過程
(1)、與二分法類似,先取整個區間的中間值mid。
mid = (left + right) / 2;
(2)、再取右側區間的中間值midmid,從而把區間分為三個小區間。
midmid = (mid + right) / 2;
(3)、我們mid比midmid更靠近最值,我們就舍棄右區間,否則我們舍棄左區間?。
比較mid與midmid誰最靠近最值,只需要確定mid所在的函數值與midmid所在的函數值的大小。當最值為最大值時,mid與midmid中較大的那個自然更為靠近最值。最值為最小值時同理。
if (cal(mid) > cal(midmid)) right = midmid; else left = mid;
(4)、重復(1)(2)(3)直至找到最值。
(5)、另一種三分寫法
1 double three_devide(double low,double up) 2 { 3 double m1,m2; 4 while(up-low>=eps) 5 { 6 m1=low+(up-low)/3; 7 m2=up-(up-low)/3; 8 if(f(m1)<=f(m2)) 9 low=m1; 10 else 11 up=m2; 12 } 13 return (m1+m2)/2; 14 }
算法的正確性:
1、mid與midmid在最值的同一側。由於凸性函數在最大值(最小值)任意一側都具有單調性,因此,mid與midmid中,更大(小)的那個 數自然更為靠近最值。此時,我們遠離最值的那個區間不可能包含最值,因此可以舍棄。
2、mid與midmid在最值的兩側。由於最值在中間的一個區間,因此我們舍棄一個區間后,並不會影響到最值
1 const double EPS = 1e-10; 2 double calc(double x) 3 { 4 // f(x) = -(x-3)^2 + 2; 5 return -(x-3.0)*(x-3.0) + 2; 6 } 7 8 double ternarySearch(double low, double high) 9 { 10 double mid, midmid; 11 while (low + EPS < high) 12 { 13 mid = (low + high) / 2; 14 midmid = (mid + high) / 2; 15 double mid_value = calc(mid); 16 double midmid_value = calc(midmid); 17 if (mid_value > midmid_value) 18 high = midmid; 19 else 20 low = mid; 21 } 22 return low; 23 }
調用ternarySearch(0, 6),返回的結果為3.0000
我們都知道 二分查找 適用於單調函數中逼近求解某點的值。
如果遇到凸性或凹形函數時,可以用三分查找求那個凸點或凹點。
下面的方法應該是三分查找的一個變形。
如圖所示,已知左右端點L、R,要求找到白點的位置。
思路:通過不斷縮小 [L,R] 的范圍,無限逼近白點。
做法:先取 [L,R] 的中點 mid,再取 [mid,R] 的中點 mmid,通過比較 f(mid) 與 f(mmid) 的大小來縮小范圍。
當最后 L=R-1 時,再比較下這兩個點的值,我們就找到了答案。
1、當 f(mid) > f(mmid) 的時候,我們可以斷定 mmid 一定在白點的右邊。
反證法:假設 mmid 在白點的左邊,則 mid 也一定在白點的左邊,又由 f(mid) > f(mmid) 可推出 mmid < mid,與已知矛盾,故假設不成立。
所以,此時可以將 R = mmid 來縮小范圍。
2、當 f(mid) < f(mmid) 的時候,我們可以斷定 mid 一定在白點的左邊。
反證法:假設 mid 在白點的右邊,則 mmid 也一定在白點的右邊,又由 f(mid) < f(mmid) 可推出 mid > mmid,與已知矛盾,故假設不成立。
同理,此時可以將 L = mid 來縮小范圍。
1 int SanFen(int l,int r) //找凸點 2 { 3 while(l < r-1) 4 { 5 int mid = (l+r)/2; 6 int mmid = (mid+r)/2; 7 if( f(mid) > f(mmid) ) 8 r = mmid; 9 else 10 l = mid; 11 } 12 return f(l) > f(r) ? l : r; 13 }
日后會以例題的形式進行詳細的講解,這個也就是入門級!