1. 程式人生 > >三分法模板講解

三分法模板講解

三分查詢

我們都知道 二分查詢 適用於單調函式中逼近求解某點的值。
如果遇到凸性或凹形函式時,可以用三分查詢求那個凸點或凹點。
下面的方法應該是三分查詢的一個變形。

如圖所示,已知左右端點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 來縮小範圍。

int SanFen(int l,int r) //找凸點  
{  
    while(l < r-1)  
    {  
        int mid  = (l+r)/2;  
        int mmid = (mid+r)/2;  
        if( f(mid) > f(mmid) )  
            r = mmid;  
        else  
            l = mid;  
    }  
    return f(l) > f(r) ? l : r;  
}  

三分查詢

一. 概念
在二分查詢的基礎上,在右區間(或左區間)再進行一次二分,這樣的查詢演算法稱為三分查詢,也就是三分法。
三分查詢通常用來迅速確定最值。

二分查詢所面向的搜尋序列的要求是:具有單調性(不一定嚴格單調);沒有單調性的序列不是使用二分查詢。
與二分查詢不同的是,三分法所面向的搜尋序列的要求是:序列為一個凸性函式。通俗來講,就是該序列必須有一個最大值(或最小值),在最大值(最小值)的左側序列,必須滿足不嚴格單調遞增(遞減),右側序列必須滿足不嚴格單調遞減(遞增)。如下圖,表示一個有最大值的凸性函式:

二、演算法過程

1)、與二分法類似,先取整個區間的中間值midmid = (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)另一種三分寫法
double three_devide(double low,double up) {  
    double m1,m2;  
    while(up-low>=eps)  {  
        m1=low+(up-low)/3;  
        m2=up-(up-low)/3;  
        if(f(m1)<=f(m2))  
            low=m1;  
        else  
            up=m2;  
    }  
    return (m1+m2)/2;  
}  

演算法的正確性:
1、mid與midmid在最值的同一側。由於凸性函式在最大值(最小值)任意一側都具有單調性,因此,mid與midmid中,更大(小)的那個 數自然更為靠近最值。此時,我們遠離最值的那個區間不可能包含最值,因此可以捨棄。
2、mid與midmid在最值的兩側。由於最值在中間的一個區間,因此我們捨棄一個區間後,並不會影響到最值

const double EPS = 1e-10;  

double calc(double x)  {  
    // f(x) = -(x-3)^2 + 2;  
    return -(x-3.0)*(x-3.0) + 2;  
}  

double ternarySearch(double low, double high)  {  
    double mid, midmid;  
    while (low + EPS < high)  {  
        mid = (low + high) / 2;  
        midmid = (mid + high) / 2;  
        double mid_value = calc(mid);  
        double midmid_value = calc(midmid);  
        if (mid_value > midmid_value)  
            high = midmid;  
        else  
            low = mid;  
    }  
    return low;  
}  

呼叫ternarySearch(0, 6),返回的結果為3.0000
二分法作為分治中最常見的方法,適用於單調函式,逼近求解某點的值。但當函式是凸性函式時,二分法就無法適用,這時三分法就可以“大顯身手”~~

程式模版如下:

double Calc(Type a){
    /* 根據題目的意思計算 */
}
void Solve(void){
    double Left, Right;
    double mid, midmid;
    double mid_value, midmid_value;
    Left = MIN; Right = MAX;
    while (Left + EPS < Right){
        mid = (Left + Right) / 2;
        midmid = (mid + Right) / 2;
        mid_area = Calc(mid);
        midmid_area = Calc(midmid);
        // 假設求解最大極值.
        if (mid_area >= midmid_area) Right = midmid;
        else Left = mid;
    }
}

如圖,人左右走動,求影子L的最長長度。
根據圖,很容易發現當燈,人的頭部和牆角成一條直線時(假設此時人站在A點),此時的長度是影子全在地上的最長長度。當人再向右走時,影子開始投影到牆上,當人貼著牆,影子長度即為人的高度。所以當人從A點走到牆,函式是先遞增再遞減,為凸性函式,所以我們可以用三分法來求解。

下面只給出Calc函式,其他直接套模版即可。

double Calc(double x){
    return (h * D - H * x) / (D - x) + x;
}

汽車拐彎問題,給定X, Y, l, d判斷是否能夠拐彎。首先當X或者Y小於d,那麼一定不能。
其次我們發現隨著角度θ的增大,最大高度h先增長後減小,即為凸性函式,可以用三分法來求解。

這裡的Calc函式需要比較繁瑣的推倒公式:
s = l * cos(θ) + w * sin(θ) - x;
h = s * tan(θ) + w * cos(θ);
其中s為汽車最右邊的點離拐角的水平距離, h為裡拐點最高的距離, θ範圍從0到90。
POJ 3301 Texas Trip
http://acm.pku.edu.cn/JudgeOnline/problem?id=3301

題意為給定n(n <= 30)個點,求出飽含這些點的面積最小的正方形。

有兩種解法,一種為逼近法,就是每次m分角度,求出最符合的角度,再繼續m分,如此進行times次,即可求出較為精確的解。(m 大概取10, times取30即可)

第二種解法即為三分法,首先旋轉的角度只要在0到180度即可,超過180度跟前面的相同的。座標軸旋轉後,座標變換為:
X’ = x * cosa - y * sina;
y’ = y * cosa + x * sina;

至於這題的函式是否是凸性的,為什麼是凸性的,我也無法給出準確的證明,希望哪位路過的大牛指點一下~~
例題更新(2010.5.5)
hdu 3400 Line belt
http://acm.hdu.edu.cn/showproblem.php?pid=3400
典型的三分法,先三分第一條線段,找到一個點,然後根據這個點再三分第二條線段即可,想出三分的思路基本就可以過了。
對於求解一些實際問題,當公式難以推匯出來時,二分、三分法可以較為精確地求解出一些臨界值,且效率也是令人滿意的。
(轉自http://hi.baidu.com/czyuan_acm/blog/item/8cc45b1f30cefefde1fe0b7e.html