三分
三分是一种用于求解单峰/单谷函数极值的搜索算法,通过每次将区间分成三份并比较两个分点的函数值来缩小搜索范围。
对于一个单峰函数,在区间 \([l,r]\)
上有唯一的极大值点。可以取两个三分点:
- \(mid1 = l + \frac{r-l}{3}\)
- \(mid2 = r - \frac{r-l}{3}\)
比较 \(f(mid1)\) 和 \(f(mid2)\) 的大小:
- 如果 \(f(mid1) <
f(mid2)\),说明极大值点在 \([mid1,r]\) 区间内,令 \(l = mid1\)
- 如果 \(f(mid1) >
f(mid2)\),说明极大值点在 \([l,mid2]\) 区间内,令 \(r = mid2\)
- 如果 \(f(mid1) =
f(mid2)\),说明极大值点在 \([mid1,mid2]\) 区间内,令 \(l = mid1, r = mid2\)
每次迭代可以将区间长度缩小为原来的 \(\frac{2}{3}\)。
对于单谷函数,只需要将比较符号反向即可求出极小值点。
三分的基本框架如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| double TrisectionMax(double l, double r) { double mid1, mid2; for(int i = 0; i < 100; i ++) { mid1 = l + (r - l) / 3; mid2 = r - (r - l) / 3; if(f(mid1) < f(mid2)) l = mid1; else r = mid2; } return l; }
double TrisectionMin(double l, double r) { double mid1, mid2; for(int i = 0; i < 100; i ++) { mid1 = l + (r - l) / 3; mid2 = r - (r - l) / 3; if(f(mid1) > f(mid2)) l = mid1; else r = mid2; } return l; }
|
示例
例:三分|函数
给定 \(n\) 个二次函数 \(f_1(x), f_2(x), \ldots, f_n(x)\)(均形如
\(ax^2 + bx + c\)),设 \(F(x) = \max\{f_1(x), f_2(x), \ldots,
f_n(x)\}\),求 \(F(x)\) 在区间
\([0, 1000]\) 上的最小值。
输入
1 2 3 4 5 6
| 2 1 2 0 0 2 2 0 0 2 -4 2
|
输出
分析:都是朝上的二次函数,画画图可以发现,两个口朝上的单峰函数取较大函数值叠加,仍然是口朝上的单峰函数,那么无数个叠加也仍然是,可以用三分求极值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| #include<cstdio> #include<algorithm>
const int maxn = 1e4 + 10;
int n;
struct Func { double a, b, c; double calc(double x) { return a * x * x + b * x + c; } } f[maxn];
double F(double x) { double ret = f[0].calc(x); for(int i = 1; i < n; i++) ret = std::max(ret, f[i].calc(x)); return ret; }
double TrisectionMin(double l, double r) { double mid1, mid2; for(int i = 0; i < 100; i ++) { mid1 = l + (r - l) / 3; mid2 = r - (r - l) / 3; if(F(mid1) > F(mid2)) l = mid1; else r = mid2; } return l; } int main() { int T; scanf("%d", &T); while(T--) { scanf("%d", &n); for(int i = 0; i < n; i++) scanf("%lf%lf%lf", &f[i].a, &f[i].b, &f[i].c); printf("%.4f\n", F(TrisectionMin(0, 1000))); } return 0; }
|