這個小程序是研一上學期的“工程優化”課程的大作業。其實這題本可以用 MATLAB 實現,但是我為了鍛煉自己薄弱的編碼能力,改為用 C 語言實現。這樣,就得自己實現矩陣的運算(加減乘除、求逆、拷貝);難點是求偏導,通過查資料,發現可以通過導數定義,即取極限的方法,來逐步逼近求得梯度;另外,沒法做到輸入任意公式,只能將公式硬編碼為函數,而求導函數需要傳入公式,就直接傳入函數指針了。思考、編碼、調試、測試共耗費兩周左右時間,完成於 2013/01/10。雖然為了認真做這個大作業而耽誤了期末考試的復習,但我不后悔做出的選擇,因為我學到了我覺得真正有用的東西。
源碼托管在 Github 上:點此打開鏈接
以下為完整的作業報告:
一、題目
用最速下降法和DFP擬牛頓法求解以下函數的最小值點以及最小值:
1.1 ,其中,
,
,
1.2 ,其中,
,
,
二、算法
2.1最速下降法(steepest descent method)
算法步驟:
(1)取初始點,精度
,令
;
(2)計算,若
,則停,
;否則轉(3);
(3)一維搜索:,
令,轉(2)。
2.2擬牛頓法(DFP)
算法步驟:
(1)取初始點,允許誤差
;
(2)求,若
,令
,算法停止;否則轉(3);
(3)令;
(4)令;
(5)求:
,令
;
(6)求,若
,令
,算法停止;否則轉(6);
(7)若,則令
,
,轉(3);
否則令,
,
計算;
令,轉(4)。
2.3成功—失敗法(用於一維搜索)
算法步驟:
(1)取初始點,初始步長
和精度
,計算
;
(2)計算;
(3)若(搜索成功),令
;
若(搜索失敗),若
,令
,停止迭代;
否者,令,轉(2);
三、語言及算法實現說明
3.1算法實現語言及平台:
C語言+VC6.0(Debug模式)。
3.2幾個部分的思考:
(1)由於實現實時輸入函數多項式比較困難,本程序將函數多項式寫成模塊,存入程序文件中,由於程序使用函數指針,故可以陸續添加函數多項式而不必修改核心算法的代碼;
(2)由於函數不同,取值范圍不同,則算法需要不同的精度和步長,才能求得精確的結果,故本程序提供接口讓用戶指定;
(3)為了實現實時輸入變量維度,本程序使用動態內存分配,建立多維數組,模擬矩陣,用於存儲多維變量;
3.3算法實現的重難點分析:
(1)偏導數的求解:本程序使用偏導數的定義,即極限方法,求解指定點的函數值;
(2)DFP算法中的計算:本程序用多維數組來模擬矩陣進行運算。
四、程序中的主要模塊說明(完整程序及注釋見附錄)
4.1待求解的兩個函數:
其中vars為多維變量,n代表維度,這兩個模塊返回函數在指定點的值。
/* 求函數1在指定點的值 */
double fun1(double **vars, int n);
/* 求函數2在指定點的值 */
double fun2(double **vars, int n);
4.2利用偏導的定義求某個點的偏導數:
其中f為指定函數,vars為多維變量,grads為梯度,n為維度,prec為用戶指定的精度;該模塊求出函數的偏導存入矩陣grads中。
/* 用極限方法求指定點的偏導/梯度 */
void differ(double (*f)(double **vars, int n), double **vars, double **grads, int n, double prec);
4.3成功—失敗法,用於一維搜索:
其中f代表指定函數,vars為多維變量,d為二維搜索的方向,n為維度,prec為用戶指定的進度,h為用戶指定的步長;
該模塊將搜索到的所對應的多維變量存入矩陣vars。
/* 成功失敗法,用於一維搜索 */
void suc_fail(double (*f)(double **vars, int n), double **vars, double **d, int n, double prec, double h);
4.4兩個核心算法:
其中fun為待解函數的標號,n為維度,prec為用戶指定的精度,h為用戶指定的用於一維搜索的步長;
這里這兩個模塊求出指定函數的最小值點和最小值並輸出。
/* 最速下降法(Speedest Descent Method)*/
void SD(int fun, int n, double prec, double h);
/* DFP擬牛頓法 */
void DFP(int fun, int n, double prec, double h);
五、程序使用說明
本程序將最速下降法和DFP法整合在一起,精度、步長、維度可由用戶指定:
(1)選擇方法(只輸入序號,‘0’退出);
(2)選擇函數(只輸入序號);
(3)輸入精度值();
(4)輸入一維搜索的步長;
(5)輸入變量維度;
(6)輸入變量的每個分量;
回車后程序開始使用指定方法對指定函數進行計算,計算過程中輸出迭代次數;
最后輸出結果:最小值點和最小值。
如下圖所示(下一頁):
六、運行結果及分析
6.1精度選擇:
(1)如下用最速下降法求函數1,精度取,步長取1,初值取(5,5,5),求解時陷入了無限迭代:
……
(2)對於(1)的輸入,僅修改精度為,僅迭代3次就求出了結果,且達到很高的精度,變量的三個分量和最優值都約等於0:
6.1.1小結
當精度值選擇太小,雖然可能得到更精確的結果,但會陷入死循環。當精度要求放松了一點,反而快速求出了精確結果,可見精度要選着適當,不可太大,也不可太小。以下試驗就選擇為精度值。
6.2一維搜索的步長選擇:
(1)如下用最速下降法求函數1,精度取,步長取0.1,初值取(3,3,3),迭代3次求出結果,但是誤差很大:
(2)針對(1),僅將步長改為0.5,迭代4次求出結果,精度很高:
(4)如下用最速下降法求函數2,精度取,步長取0.5,
初值取(300,300,300),迭代9次求出結果,但是誤差很大:
(5)針對(4),僅將步長改為30,迭代17次求出結果,雖然結果與理想值0還是有一些誤差,但比(4)的結果精確了很多:
6.2.1小結
一維搜索的步長也要選擇適當,否者求出的結果誤差很大。從以上對比可以看出,步長的選取要根據自變量的取值進行相應的調整:函數F1的,變量取3,步長h取0.5時誤差較小;函數F2的
,變量取300,步長h取30時誤差較小,步長h取值為變量x取值的10%左右時誤差較小。
6.3比較最速下降法和DFP法:
6.3.1求解函數F1:精度取,步長取0.5,變量分別取(-5,-5,-5)、(5,5,5)
(1) 最速下降法
(2) DFP
6.3.2求解函數F2:精度取,步長取50,變量取(500,500,500)
(1)最速下降法
(2)DFP
6.3.3小結
由以上兩組對比可看出:
(1) 對於函數F1和F2,DFP算法都比最速下降法迭代次數多;
(2) 對於函數F1和F2,DFP算法都比最速下降法結果精確;