文章目錄
最佳化理論,第八章 無約束最佳化的直接法
解析法 vs 直接法
產生背景
解析法,需要計算目標函數的一階導數,甚至二階導數。
目標函數的解析運算式比較複雜,或者難以用明顯的解析式表達出來時,其導數難求或者無法求出。
?
給出一些只涉及目標函數值的計算、不涉及目標函數求導的求解方法。
僅僅利用目標函數值的資訊直接建立搜尋求解的方法通常稱為直接搜尋法(direct search method),簡稱直接法。
儘管演算法的效果有時候不理想,但是構思直觀、使用方便、效果穩定,實際工作者很願意採用。
8.1座標輪換法
8.2模式搜尋法
8.3旋轉方向法
8.4 Powell法
8.5單純形調優法
講述要點:基本思想、操作步驟、編程實現、方法評價
總體比較:
中文名,英文名 提出作者(時間) |
一句話概括 |
座標輪換法 Univariate search technique D’esopo (1995) |
每一次迭代,以一個變數的座標軸方向作為搜尋方向,將N維的最佳化問題轉化為一維搜尋問題。 |
模式搜尋法 Pattern search method Hooks 和 Jeeves(1961) |
每一次迭代,交替進行軸向移動和模式移動。 軸向移動:探測下降的有利方向 模式移動:沿著有利方向加速移動 |
旋轉方向法 Rotating direction method Rosenbrock(1960) |
每一次迭代,採用變步長的軸向移動,然後利用軸向的旋轉產生一組新的方向,作為下一次迭代的軸向。 |
8.1座標輪換法
做一維搜尋,究竟應該把哪個座標軸方向放在前面,哪個座標軸方向放在後面?
這會對演算法的效率產生較大的影響!
實際問題中,可按照各個因素(變數)對實驗結果影響的大小,依次從前往後排列座標軸方向。
(分析各個因子對實驗結果影響的大小,方法有層次分析法、專家打分法等。 大家懂的……)
演算法描述
方法評價:
簡單,容易實現。
當維數增加時,效率明顯下降。
收斂慢,以振蕩方式逼近最優點。
受目標函數的形態影響很大
8.2模式搜尋法
首先,探測有利的下降方向:軸向移動(axis direction move)
然後,沿著有利方向加速移動:模式移動(pattern move)
演算法描述
輸入 輸出
說明 初始值 收縮因子 要求精度 迭代次數 自變數X 函數值
書上的 0,0 0.25 0.1 2 0.375 , 0.125 -1.016
改變初始值 1,1 0.25 0.1 2 1.375 , 0.750 -2.703(好點)
改變精度 0,0 0.25 0. 001 8 0.498 , 0.248 -1.308(不好)
改變初始值和收縮因子 1,1 0.5 0.1 3 1.875 , 1.000 -2.984(好點)
三個都改 1,1 0.5 0.001 9 1.998 , 1.000 -3.000(很好)
演算法評價:
可以體會到,選取初值是個大問題,確定收縮因子也比較棘手。
通過探測移動和模式移動,逐步逼近最優點,需要計算很多次函數值。(電腦不怕麻煩的~)
8.3旋轉方向法
變步長軸向移動
軸向旋轉,產生新的軸向
“旋轉方向法,Rotating direction method ,是Rosenbrock在1960年提出來的。”
圖書館:沒找到“旋轉方向法”
陳開周,最佳化計算方法,西北電訊工程學院出版社,1985.10 P253 Rosenbrock座標輪換法,於1960年提出。
陳開周:每次迭代分為兩部分,
(1)沿n個正交方向進行試探
(2)決定n個新的正交方向以改善原搜尋方向,每次迭代的試探方向組是不同的。每一次迭代,採用變步長的軸向移動,然後利用軸向的旋轉產生一組新的方向,作為下一次迭代的軸向。
演算法描述
代碼1:一維搜尋之0.618法
P140用0.618法求解 P136#include<stdio.h>#include<math.h>//解析式原型float f(float x){return (x*x*x-2*x+1);}//0.618法float f_6_2(){float a=-2;//低區間float b=2;//高區間float epsilon=0.001 ;//精度float tao=0.618;//tao的值float lambda=a+(1-tao)*(b-a);float miu=a+tao*(b-a);while(fabs(lambda-miu)>=epsilon){float f1=f(lambda);//低者的函數值float f2=f(miu);//高者的函數值if(f1<f2)//向左搜尋{b=miu;miu=lambda;lambda=a+(1-tao)*(b-a);}else//向右搜尋{a=lambda;lambda=miu;miu=a+tao*(b-a);}}float t=(lambda+miu)/2.0;return t;}int main(){printf("%f\n",f_6_2());return 1; }運行結果:0.817043參考P137,該結果比較好
代碼2:模式搜尋法
// 模式搜尋法.cpp : 定義控制台應用程式的進入點。////嘗試探測每一個方向;若下降了,採取下降的建議;否則不採取建議。#include "stdafx.h"#include <iostream>using namespace std;double f(double x1,double x2)//問題的解析式{//return ((x1-1)*(x1-1)+5*(x1*x1-x2)*(x1*x1-x2));return (x1*x1+x2*x2-3*x1-x1*x2);}int _tmain(int argc, _TCHAR* argv[]){double x1=1;//初始值double x2=1;//初始值double t1=x1;//探測的初始值,變數x1的探頭double t2=x2;//探測的初始值,變數x2的探頭double alpha=0.5;//收縮因子??double epsilon=0.001;//精度double temp_x1;double temp_x2;int times=1;//迭代次數do {printf("第%2d 輪計算:",times);//第一個方向if (f(t1+alpha,t2)<f(t1,t2)){t1=t1+alpha;}if (f(t1-alpha,t2)<f(t1,t2)){t1=t1-alpha;}//第二個方向if (f(t1,t2+alpha)<f(t1,t2)){t2=t2+alpha;}if (f(t1,t2-alpha)<f(t1,t2)){t2=t2-alpha;}//模式移動if (f(t1,t2)<f(x1,x2)){temp_x1=x1;temp_x2=x2;x1=t1;x2=t2;t1=x1+x1-temp_x1;t2=x2+x2-temp_x2;}//檢測上述的探測是否成功。若成功,繼續;否則,復原if ((x1!=t1||x2!=t2)){t1=x1;t2=x2;}else{break;}printf("t=( %5.3f , %5.3f )\n",t1,t2);//縮短步長alpha=alpha/2;times++;} while (alpha>=epsilon);printf("結果值:%5.3f\n",f(t1,t2));system("pause");return 0;}
代碼3:旋轉方向法
// 旋轉方向法.cpp : 定義控制台應用程式的進入點。//#include "stdafx.h"#include <iostream>#include <math.h>using namespace std;double f(double x1,double x2){return (x1*x1+x2*x2-3*x1-x1*x2);}int main(void){double x1=0;double x2=0;double alpha=0.5;//收縮因子double beta=3;//放大因子double delta1=1;//x1的步長,初始為1double delta2=1;//x2的步長,初始為1double epsilon=0.0001;double y1;//x1的參考點double y2;//x2的參考點double z1;//暫時儲存z1的值double z2;//暫時儲存z2的值y1=x1;y2=x2;z1=x1;z2=x2;for(int times=1;times<=10;times++) {cout<<endl<<"第"<<times<<"次迭代:"<<endl;//第一個方向if (f(y1+delta1,y2)<f(y1,y2)){y1=y1+delta1;delta1=delta1*beta;//放大cout<<"第一個方向,放大"<<beta<<"倍,delta1="<<delta1<<endl;}else{delta1=delta1*(-alpha);//縮小cout<<"第一個方向,縮小"<<(-alpha)<<"倍,delta1="<<delta1<<endl;}//第二個方向if (f(y1,y2+delta2)<f(y1,y2)){y2=y2+delta2;delta2=delta2*beta;//放大cout<<"第二個方向,放大"<<beta<<"倍,delta2="<<delta2<<endl;}else{delta2=delta2*(-alpha);//縮小cout<<"第二個方向,縮小"<<(-alpha)<<"倍,delta2="<<delta2<<endl;}if (f(y1,y2)<f(z1,z2)){z1=y1;z2=y2;cout<<"下降方向,更新解的值"<<endl;}} cout<<endl<<"最終結果 x= "<<z1<<" , "<<z2<<endl;system("pause"); return 0;}結果:
第1次迭代:
第一個方向,放大3倍,delta1=3
第二個方向,縮小-0.5倍,delta2=-0.5
下降方向,更新解的值
第2次迭代:
第一個方向,縮小-0.5倍,delta1=-1.5
第二個方向,縮小-0.5倍,delta2=0.25
第3次迭代:
第一個方向,縮小-0.5倍,delta1=0.75
第二個方向,放大3倍,delta2=0.75
下降方向,更新解的值
第4次迭代:
第一個方向,放大3倍,delta1=2.25
第二個方向,放大3倍,delta2=2.25
下降方向,更新解的值
第5次迭代:
第一個方向,縮小-0.5倍,delta1=-1.125
第二個方向,縮小-0.5倍,delta2=-1.125
第6次迭代:
第一個方向,縮小-0.5倍,delta1=0.5625
第二個方向,縮小-0.5倍,delta2=0.5625
第7次迭代:
第一個方向,縮小-0.5倍,delta1=-0.28125
第二個方向,縮小-0.5倍,delta2=-0.28125
第8次迭代:
第一個方向,縮小-0.5倍,delta1=0.140625
第二個方向,縮小-0.5倍,delta2=0.140625
第9次迭代:
第一個方向,放大3倍,delta1=0.421875
第二個方向,縮小-0.5倍,delta2=-0.0703125
下降方向,更新解的值
第10次迭代:
第一個方向,縮小-0.5倍,delta1=-0.210938
第二個方向,放大3倍,delta2=-0.210938
下降方向,更新解的值
最終結果 x= 1.89063 ,0.929688
迴圈次數越多,值越準確。
收穫:
好書,啟發思維;差書,混淆思路。