Python和Java代码实现:进退法确定搜索区间

引言

之前的文章中介绍过,黄金分割法可以求解如下的一维最优化问题:在一个搜索区间内,包含了目标函数的极小值点。如果此时只有函数的一个初始点 x 0 x_0 x0,但是并不知道极小值所在搜索区间,那么可以先使用本文即将介绍的进退法来确定搜索区间,然后再使用黄金分割法得到极值。

进退法原理

进退法的理论依据是:假定 f ( x ) f(x) f(x)为单峰函数,只有一个极小值,且区间 [ a , b ] [a, b] [a,b]为极小值的一个搜索区间,那么对于任意 x 1 , x 2 ∈ [ a , b ] x_1,x_2\in[a,b] x1,x2[a,b],如果 f ( x 1 ) < f ( x 2 ) f(x_1) < f(x_2) f(x1)<f(x2),则搜索区间可以收缩为 [ a , x 2 ] [a,x_2] [a,x2];如果 f ( x 1 ) > f ( x 2 ) f(x_1) > f(x_2) f(x1)>f(x2),则搜索区间可以收缩为 [ x 1 , b ] [x_1,b] [x1,b]

因此,如果给定初始点 x 0 x_0 x0和搜索步长 h h h的情况下,首先以初始步长向前搜索一步,并计算对应的值 f ( x 0 + h ) f(x_0+h) f(x0+h):

(1)如果 f ( x 0 ) < f ( x 0 + h ) f(x_0) < f(x_0+h) f(x0)<f(x0+h),可以反推出搜索区间为 [ x ~ , x 0 + h ] [\tilde{x},x_0+h] [x~,x0+h],其中 x ~ \tilde{x} x~虽然暂时未知,但是右端点可以确定为 x 0 + h x_0+h x0+h;此时再后退一步计算 f ( x 0 − λ h ) f(x_0-\lambda h) f(x0λh),直到找到一个 λ \lambda λ,使得 f ( x 0 − λ h ) > f ( x 0 ) f(x_0-\lambda h) > f(x_0) f(x0λh)>f(x0),从而确定搜索区间为 [ f ( x 0 − λ h ) , f ( x 0 + h ) ] [f(x_0-\lambda h),f(x_0+h)] [f(x0λh),f(x0+h)]

(2)如果 f ( x 0 ) > f ( x 0 + h ) f(x_0) > f(x_0+h) f(x0)>f(x0+h),可以反推出搜索区间为 [ x 0 , x ~ ] [x_0,\tilde{x}] [x0,x~],其中 x ~ \tilde{x} x~待求,此时前进一步计算 f ( x 0 + λ h ) f(x_0+\lambda h) f(x0+λh),直到找到一个 λ \lambda λ,使得 f ( x 0 + λ h ) > f ( x 0 + h ) f(x_0+\lambda h) > f(x_0+h) f(x0+λh)>f(x0+h),从而确定搜索区间为 [ f ( x 0 ) , f ( x 0 + λ h ) ] [f(x_0),f(x_0+\lambda h)] [f(x0),f(x0+λh)]

(3)如果 f ( x 0 ) = f ( x 0 + h ) f(x_0) = f(x_0+h) f(x0)=f(x0+h),因为 f ( x ) f(x) f(x)是单峰函数,所以搜索区间可以直接确定为 [ f ( x 0 ) , f ( x 0 + h ) ] [f(x_0),f(x_0+h)] [f(x0),f(x0+h)]

python代码实现

一维最优化问题还是用黄金分割法中的实例:
m i n ( t 2 − 5 t + 8 ) min(t^2-5t+8) min(t25t+8)
该问题最小值为1.75,其对应的 t = 2.5 t=2.5 t=2.5

实现以上进退法的Python代码如下所示。

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


# 进退法:确定搜索区间
def advance_and_retreat(func, x, h):

    if abs(func(x) - func(x + h)) <= 1e-6:
        # 第三种情况
        x_min, x_max = x, x + h
    elif func(x) < func(x + h):
        # 第一种情况
        x_max = x + h
        lamb = 1
        while func(x - lamb * h) < func(x):
            lamb += 1
        x_min = x - lamb * h
    else:
        # 第二组情况
        x_min = x + h
        lamb = 2
        while func(x + lamb * h) < func(x + h):
            lamb += 1
        x_max = x + lamb * h

    return x_min, x_max


if __name__ == '__main__':

    # 步长
    step = 0.1

    # 第一种情况实例
    x1 = 2
    lb1, ub1 = advance_and_retreat(f, x1, step)
    print('S1: x0 = {}, lb = {}, ub = {}'.format(x1, lb1, ub1))

    # 第二种情况实例
    x2 = 4
    lb2, ub2 = advance_and_retreat(f, x2, step)
    print('S2: x0 = {}, lb = {}, ub = {}'.format(x2, lb2, ub2))

    # 第三种情况实例
    x3 = 2.45
    lb3, ub3 = advance_and_retreat(f, x3, step)
    print('S3: x0 = {}, lb = {}, ub = {}'.format(x3, lb3, ub3))

主函数中,选取了3种不同的初值,分别对应了原理中所描述的3种情况。运行以上代码后,可以得到以下结果。显然每一个区间内都包含了极值点2.5,即都得到了正确的搜索区间。

S1: x0 = 2, lb = 2.1, ub = 2.9
S2: x0 = 4, lb = 1.0, ub = 4.1
S3: x0 = 2.45, lb = 2.45, ub = 2.5500000000000003

Java代码实现

Java代码如下所示。

public class AdvanceAndRetreat {
    public static void main(String[] args) {
        // 步长
        double step = 0.1;

        // 第一种情况实例
        double x1 = 2.0;
        Intervals interval1 = advanceAndRetreatMethod(x1, step);
        System.out.println("S1: x0 = " + x1 + ", lb = " + interval1.lb +  ", ub = " + interval1.ub);

        // 第二种情况实例
        double x2 = 4;
        Intervals interval2 = advanceAndRetreatMethod(x2, step);
        System.out.println("S2: x0 = " + x2 + ", lb = " + interval2.lb +  ", ub = " + interval2.ub);

        // 第三种情况实例
        double x3 = 2.45;
        Intervals interval3 = advanceAndRetreatMethod(x3, step);
        System.out.println("S2: x0 = " + x3 + ", lb = " + interval3.lb +  ", ub = " + interval3.ub);

    }

    // 进退法:确定搜索区间
    private static Intervals advanceAndRetreatMethod(double x, double h) {

        double lb = 0, ub = 0;

        if (Math.abs(func(x) - func(x+h)) <= 1e-6) {
            // 第三种情况
            lb = x;
            ub = x + h;
        }
        else if (func(x) < func(x+h)) {
            // 第一种情况
            ub = x + h;
            int lamb = 1;
            while (func(x-lamb * h) < func(x)) {
                lamb += 1;
            }
            lb = x - lamb * h;
        }
        else {
            // 第二种情况
            lb = x + h;
            int lamb = 2;
            while (func(x+lamb * h) < func(x + h)) {
                lamb += 1;
            }
            ub = x + lamb * h;
        }

        // 构造搜索区间对象
        Intervals interval = new Intervals();
        interval.lb = lb;
        interval.ub = ub;

        return interval;

    }

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

    // 搜索区间对象
    private static class Intervals {
        double lb;
        double ub;
    }

}

运行以上代码后,可以得到

S1: x0 = 2.0, lb = 2.1, ub = 2.9
S2: x0 = 4.0, lb = 1.0, ub = 4.1
S2: x0 = 2.45, lb = 2.45, ub = 2.5500000000000003
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值