三分法
简介
三分法求单峰(或单谷)的极值是二分法的简单拓展,单峰(或单谷)可以简单的理解为二次函数含最值的区间,左右两边严格单调,对于这样的二次函数:y = ax2 + bx + c(a≠0),最值在 x = - b/2a 处取得,则给定一个区间,该区间包含这个 x 值,就可以使用三分法求解取得最值的 x 值。
原理
拿上面的二次函数举例,若二次项系数a大于0,则该二次函数的图像是一个单谷,给定区间包含最值点 x = - b/2a ,则我们可以将该区间三等分,左三等分点为
m
i
d
L
midL
midL ,右三等分点为
m
i
d
R
midR
midR 。
若
m
i
d
L
midL
midL 对应的函数值大于
m
i
d
R
midR
midR 对应的函数值,则有两种情况:第一种是
m
i
d
L
midL
midL 和
m
i
d
R
midR
midR 都在最值点的左边,第二种是
m
i
d
L
midL
midL 在最值点的左边,而
m
i
d
R
midR
midR 在最值点的右边。可见,两种情况都是
m
i
d
L
midL
midL 在最值点左边。
若
m
i
d
L
midL
midL 对应的函数值小于
m
i
d
R
midR
midR 对应的函数值,则有两种情况:第一种是
m
i
d
L
midL
midL 和
m
i
d
R
midR
midR 都在最值点的右边,第二种是
m
i
d
L
midL
midL 在最值点的左边,而
m
i
d
R
midR
midR 在最值点的右边。可见,两种情况都是
m
i
d
R
midR
midR 在最值点右边。
结合上面两个推断,可以得出一个策略:对于二次项系数a大于0的二次函数,当
m
i
d
L
midL
midL 对应的函数值大于
m
i
d
R
midR
midR 对应的函数值时,让子区间的左端点取
m
i
d
L
midL
midL ;当
m
i
d
L
midL
midL 对应的函数值小于
m
i
d
R
midR
midR 对应的函数值时,让子区间的右端点取
m
i
d
R
midR
midR 。这样可以保证在缩小区间的时候让最值点一直在区间内。
对于二次项系数a小于0的二次函数,结论刚好是相反的。
复杂度
每次将长度为 n n n 的区间缩小为 1/3 ,算法的时间复杂度为 O(log3n) 。
建模
对于三分法能解决的问题,最难的是建立一个单峰(或单谷)函数,其它的有如下的模版:
// type_name是基本数据类型中 整型 或 浮点型 的一种,根据精度和题目而改变
type_name func_x(type_name x); // 根据传入的x取其对应的函数值
type_name left, right, midL, midR;
// 接收left和right的值
while ( /* 条件,实数三分和整数三分不同 */ )
{
midL = left + (right - left) / 3;
midR = right - (right - left) / 3;
if (func_x(midL) > func_x(midR))
// left = midL; 或 right = midR; 根据函数的单调性而改变
else
// right = midR; 或 left = midL; 根据函数的单调性而改变
}
// 输出结果
实数三分
对于实数三分,我们可以提前设置一个精度,当区间长度(即 右端点 - 左端点)小于精度时,退出循环,区间左端点就是结果。
例题如下:
【模板】三分 (P3382)
整数三分
对于整数三分,当区间长度小于
2
2
2 时,退出循环,然后在区间的三个值对应的函数值中枚举最值。
例题如下:
期末考试 (洛谷P3745)