三分法求單峰(單谷)函式極值
阿新 • • 發佈:2018-12-14
what is 三分法
對於二分,相信你一定十分熟悉。就是在一個具有單調性序列上查詢你所需要的數字。由於其單調性,你每一次在查詢是就可以將規模縮小一半,大致就是:
1.假設這個數列單調遞增
2.維護一個區間左端點\(l\),區間右端點r和中間點\(mid\)
3.如果\(mid\)比想要的值小,則左邊肯定不可以,那麼\(l=mid\)
2.如果\(mid\)比想要的值大,則右邊肯定不可以,那麼\(r=mid\)
因此大致就可以這麼寫:
while (l+1<r) { int mid=l+r>>1; if (v<a[mid) r=mid; else l=mid; }
不保證程式碼正確,但是具體思想就是這樣。
三分也一樣啊:
對於一段拋物線(極值的一邊單調遞增,極值的一邊單調遞減)我們就可以把它分成三段,根據其影象特性來求解。
三分法求二次函式峰值
對於三分,我們用左端點\(lmid\)和\(rmid\)進行維護,將這個影象分成三段。並且影象區間的左右端點分別是\(l<r.\)則我們可以選擇這麼考慮:(以二次函式\(y=-5x^{2}+8x-1\)為例)
如圖所示:
當\(lmid\)處於\(A\)點,\(rmid\)處於\(B\)點時:可將左端點\(l\)縮到\(lmid\),右端點不變以保證極值存在。
當\(lmid\)處於\(A\)點,\(rmid\)
同理,
當\(lmid\)處於\(C\)點,\(rmid\)處於\(D\)點時:可將右端點\(r\)縮到\(rmid\),左端點不變以保證極值存在。
當\(lmid\)處於\(B\)點,\(rmid\)處於\(B\)點時:照樣可以將\(r\)縮到\(rmid\)。
故得到結論:
\[f(l)<f(r)→l=lmid\]
\[f(l)≥f(r)→r=rmid\]
然後就進行簡單的程式碼實現:
#include<bits/stdc++.h> using namespace std; double a,b,c; inline double f(double x) { return a*x*x+b*x+c; } int main(void) { cin>>a>>b>>c; //形如y=ax^2+by+c的二次函式 double l=-1e9,r=1e9; while (l+1e-9<r) { double lmid=l+(r-l)/3.0; //影象上位於1/3部分的靠左的mid值 double rmid=l+(r-l)/3.0*2.0; //影象上位於2/3部分的靠右的mid值 if (f(lmid)<f(rmid)) l=lmid; else r=rmid; //求單峰極值 } cout<<"X="<<l<<'\n'; cout<<"Y="<<f(l); }
二次函式求單谷谷值 & 高次函式應用
通過畫圖和分類討論\(a<0\)的情況,不難得出:
\[f(l)>f(r)→l=lmid\]
\[f(l)≤f(r)→r=rmid\]
程式碼實現只要if內反一下即可:
#include<bits/stdc++.h>
using namespace std;
double a,b,c;
inline double f(double x) {
return a*x*x+b*x+c;
}
int main(void)
{
cin>>a>>b>>c;
//形如y=ax^2+by+c的二次函式
double l=-1e9,r=1e9;
while (l+1e-9<r)
{
double lmid=l+(r-l)/3.0;
//影象上位於1/3部分的靠左的mid值
double rmid=l+(r-l)/3.0*2.0;
//影象上位於2/3部分的靠右的mid值
if (f(lmid)>f(rmid)) l=lmid;
else r=rmid;
//求單峰極值
}
cout<<"X="<<l<<'\n';
cout<<"Y="<<f(l);
}
如果需要高次函式過其它影象,只要在f內稍作修改即可。