文章目录

  • 切线法原理
  • 代码实现
  • Python代码
  • Java代码
  • 求解实例


切线法原理

在上一篇文章中,我们使用黄金分割法求解了一维最优化问题。

本文介绍另一种求解该问题的算法:切线法。

该方法的逻辑是:首先在python 黄金分割_python 黄金分割内随机选择一点python 黄金分割_java_02,沿着该点做一条切线,切线和python 黄金分割_迭代_03轴的交点被选择为下一个点python 黄金分割_迭代_04,如此反复直至python 黄金分割_java_05小于给定阈值,即得到最优解。

python 黄金分割_最优解_06

接下来,我们看一下该方法的数学原理。
针对点python 黄金分割_java_02,切线方程为
python 黄金分割_迭代_08
python 黄金分割_迭代_09,得到对应的python 黄金分割_迭代_04
python 黄金分割_迭代_11

随着python 黄金分割_迭代_12的增大,python 黄金分割_最优解_13如果能趋向于0,python 黄金分割_java_05就能小于任意给定阈值,最终得到最优解。
当然,如果待优化函数一阶或者二阶不可导,那么切线法就无法使用了。

代码实现

Python代码

以下的tangent_method函数为切线法的Python代码。
除待优化函数python 黄金分割_python_15外,代码中还定义了对应的一阶导数python 黄金分割_最优解_16和二阶导数python 黄金分割_java_17

# 待优化函数
def f(t):
    return t ** 2 - t * 5 + 8


# 待优化函数一阶导数
def d_f(t):
    return 2 * t - 5


# 待优化函数二阶导数
def d_2_f(t):
    return 2


# 切线法
def tangent_method(x, eps):

    cnt = 0
    while abs(-d_f(x) / d_2_f(x)) > eps:
        # 更新x,并增加迭代次数
        x += -d_f(x) / d_2_f(x)
        cnt += 1

    return x, f(x), cnt


if __name__ == '__main__':
    # 参数设置
    left_point = 1
    right_point = 7
    min_interval_value = 0.1

    # 选取初始点
    x0 = 6
    # 调用切线法求解最小值
    best_x, best_y, iter_cnt = tangent_method(x0, min_interval_value)
    # 输出最优解
    print('best_x: {}, best_y: {}, iter_cnt: {}.'.format(best_x, best_y, iter_cnt))

Java代码

以下的tangentMethod函数为切线法的Java代码。

public class TangentMethod {

    public static void main(String[] args) {
        // 参数设置
        int leftPoint = 1;
        int rightPoint = 7;
        double minIntervalValue = 0.1;

        double x0 = 6.0;
        Solution best_solution = tangentMethod(x0, minIntervalValue);
        System.out.println("best_x: " + best_solution.best_x);
        System.out.println("best_y: " + best_solution.best_y);
        System.out.println("cnt: " + best_solution.cnt);
    }

    // 切线法
    private static Solution tangentMethod(double x, double eps) {
        // 统计迭代次数
        int cnt = 0;

        while (Math.abs(df(x) / d2f(x)) > eps) {
            // 更新x,并增加迭代次数
            x -= df(x) / d2f(x);
            cnt ++;
        }

        // 构造最优解对象
        Solution best_solution = new Solution();
        best_solution.best_x = x;
        best_solution.best_y = f(x);
        best_solution.cnt = cnt;

        return best_solution;
    }

    // 待优化函数
    private static double f(double t) {
        return t * t - t * 5 + 8;
    }

    // 待优化函数一阶导数
    private static double df(double t) {
        return t * 2 - 5;
    }

    // 待优化函数二阶导数
    private static double d2f(double t) {
        return 2;
    }

    // 解对象
    private static class Solution {
        double best_x;
        double best_y;
        int cnt;
    }
}

求解实例

无论运行Python程序还是Java程序,可以得到最优解如下:

best_x: 2.5, best_y: 1.75, iter_cnt: 1.

从结果上可以看出,切线法只需要迭代一次即可得到最优解; 而使用黄金分割法需要迭代的次数为9。 这主要是因为切线法使用了待优化函数更多的信息,包括一阶和二阶导数,所以计算效率更高,这在文章中也有提到过该理论。

此外,相比黄金分割法,切线法并不依赖左右端点的值,但是会对初值更加敏感(虽然文中的实例对初值不敏感),这也是值得注意的。