一維搜索法-- 進退法與黃金分割法求一元二次函數最小值(Java實現)

說明:

  1. 進退法 – 用於確定函數最值搜索範圍
  2. 黃金分割法– 在確定範圍內搜索函數最值

算法比較簡單,基本通過代碼就能理解

public class 進退法 {
    /**
     * 求解最小值的函數
     * 
     * @param x
     * @return
     */
    public static double f(double x) {
        double y = (x + 3) * (x - 3);
        System.out.println(String.format("f(%f)=%f", x, y));
        return y;
    }

    /**
     * 進退法求函數極值範圍
     * 
     * <pre>
     * 初始化a0=0, 步長 h0=0
     * 1. h=h0, a1=a0,a2=a1+h
     * 2. 若g(a2)<=g(a1)    goto 4
     * 3. 交換a1 和a2, 並令 h=-h
     * 4. h=2h, 令a3=a2+h
     * 5. 若g(a3)>g(a2) 已找到極小架子{ a1, a2, a3} ; 否則令a1=a2, a2=a3  goto 4
     * </pre>
     * 
     * @param x0
     * @param h
     * @return
     */
    public static double[] jtf(double x0, double h) {
        System.out.println("進退法------確定函數最小值範圍!");
        double x1 = x0, x2 = x1 + h, x3;
        double y2 = f(x2);
        if (f(x1) < y2) {// 如果y2 > y1 表明方向反了,通過h符號修改方向,同時交換x1與x2
            h = -h;
            x1 = x2;
            x2 = x0;
        }
        // 前進
        while (true) {
            x3 = x2 + 2 * h;
            double y3 = f(x3);
            if (y3 > y2) {// 說明已經找到了符合條件的點(高-低-高)
                break;
            }
            x1 = x2; // 每次前進都要重新修改範圍( x1 與x2 前進1布)
            x2 = x3;
            y2 = y3;// 臨時變量,避免重複計算
        }
        System.out.println(String.format("x1=%f,x2=%f,x3=%f", x1, x2, x3));
        return new double[] { Math.min(x1, x3), Math.max(x1, x3) };
    }

    public static void goldSearch(double start, double end, double eps) {
        System.out.println("黃金分割法------根據範圍搜索函數最小值!");
        // 初始化初始搜索範圍
        double a, b;
        b = Math.max(start, end);
        a = Math.min(start, end);

        double t1, t2, f1, f2;
        double ranta = 0.618034f;

        // 黃金分割點 t1 和 t2
        t1 = b - ranta * (b - a);
        t2 = a + ranta * (b - a);
        f1 = f(t1);
        f2 = f(t2);

        while (t2 - t1 > eps) {
            if (f1 <= f2)
                b = t2;
            else
                a = t1;
            t1 = b - ranta * (b - a);
            t2 = a + ranta * (b - a);
            f1 = f(t1);
            f2 = f(t2);
        }

        double x, y;
        if (f1 > f2) {
            x = t2;
            y = f2;
        } else {
            x = t1;
            y = f1;
        }
        System.out.println(String.format("點:%f 處取最小值:%f", x, y));
    }

    public static void main(String[] args) {
        double[] range = jtf(10, 1d);
        System.out.println(String.format("函數最小值範圍:(%f,%f)", range[0], range[1]));
        goldSearch(range[0], range[1], 0.01);
    }
}

結果:

進退法------確定函數最小值範圍!
f(11.000000)=112.000000
f(10.000000)=91.000000
f(8.000000)=55.000000
f(6.000000)=27.000000
f(4.000000)=7.000000
f(2.000000)=-5.000000
f(0.000000)=-9.000000
f(-2.000000)=-5.000000
x1=2.000000,x2=0.000000,x3=-2.000000
函數最小值範圍:(-2.000000,2.000000)
黃金分割法------根據範圍搜索函數最小值!
f(-0.472136)=-8.777088
f(0.472136)=-8.777088
f(-1.055728)=-7.885438
f(-0.472136)=-8.777088
f(-0.472136)=-8.777088
f(-0.111456)=-8.987578
f(-0.111456)=-8.987578
f(0.111456)=-8.987578
f(-0.249224)=-8.937888
f(-0.111456)=-8.987578
f(-0.111456)=-8.987578
f(-0.026311)=-8.999308
f(-0.026311)=-8.999308
f(0.026311)=-8.999308
f(-0.058834)=-8.996539
f(-0.026311)=-8.999308
f(-0.026311)=-8.999308
f(-0.006211)=-8.999961
f(-0.006211)=-8.999961
f(0.006211)=-8.999961
f(-0.013889)=-8.999807
f(-0.006211)=-8.999961
點:-0.006211 處取最小值:-8.999961

參考資料:
1. Matlab代碼,一維搜索用進退法確定搜索區間
2. 最優化第二講——一維搜索法(黃金分割法和java實現)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章