在使用二分法時必須保證數列是有序的,因此對應到數學問題上實際上解決的是單調函數的求解問題。通過二分法查找其中一個元素key,則轉換爲數學問題求f(x)=key的解。
但是一旦數列不是單調的,那麼二分法就無法使用了,因爲循環判斷中不能確定左右區間的劃分,於是對於函數有凹凸性的情況,便引入了三分法的使用場景。
欲求某個函數的極值點及極值,將區間[l,r]分爲三部分,需要兩個分界點m1和m2,如下
根據上述函數圖像觀察到f(m1)>f(m2),即m2離極小值點更近,爲了讓區間不斷趨近極小值點,r不動,將l趨向m1,所以有l=m1。
當f(m1)<f(m2),即m1點離極小值點更近,此時縮小區間則應讓l不動,r趨近到m2,有r=m2
若f(m1)=f(m2)則同理,爲了簡化代碼,我們在這裏將相等的情況與小於的情況合併
這裏我們求函數的極小值即極小值點來試試,代碼如下:
/**
* 函數方程(可自己定義)
* @param x
* @return
*/
private static double function(double x){
return x*x+3*x+6;
}
/**
* 三分法
* @param l:區間左邊界
* @param r:區間右邊界
* @return 極值點的位置
*/
private static double tripleSearch(double l,double r){
while(l+EIS<r){ //EIS=10e-6,即10的-6次方; 因爲所有參數爲雙精度型,不能直接用l<r
double mid1=(l+r)/2;
double mid2=(mid1+r)/2;
if(function(mid1)<=function(mid2)){
r=mid2;
}else {
l=mid1;
}
}
return l;
}
main函數:
public static void main(String[] args) {
System.out.println(String.format("%.2f",tripleSearch(-2,1)));
System.out.println(String.format("%.2f",function(tripleSearch(-2,1))));
}
運行結果如下,經過驗算,正確,over!