說明:
- 進退法 – 用於確定函數最值搜索範圍
- 黃金分割法– 在確定範圍內搜索函數最值
算法比較簡單,基本通過代碼就能理解
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實現)