1. 程式人生 > >【工程優化】一維搜尋方法

【工程優化】一維搜尋方法

一維搜尋方法的分類如下:

        這篇文章主要講解黃金分割法、二分法、牛頓法這三種一維搜尋方法。黃金分割法只用到原函式,二分法用到函式的一階導,牛頓法用到函式的二階導。由於本文主要對研一上學期的課程中的部分演算法進行程式實現,理論部分大多參考上課的課件

黃金分割法

    基本概念:

演算法思想:

演算法流程圖及優缺點如下:


二分法

 基本思想:


牛頓法

基本思想:



演算法流程圖:


具體實現:

下面我們通過程式具體實現,在程式中,我們設定原函式都是f(x)=sinx/x,搜尋區間都是[0,1],牛頓法中假設初始值設為1,具體程式如下所示
#include<stdio.h>
#include<math.h>
/********************函式的定義、一階導、二階導的模組 BEGIN*************************/
/*****************************\
輸入:x為自變數
輸出:x自變數對應的函式值
\*****************************/
double Function(double x)
{
    return (x-0.5)*(x-0.5);//這裡填寫函式式f(x),根據自己的函式修改
}
/*****************************\
輸入:x為自變數
輸出:x自變數對應的一階導數值
\*****************************/
double Derivative(double x)//求函式的一階導數
{
    double eps=0.0000001;//精度控制
    double dx=0.5;//設定初始的間隔,太大需要迭代多次,太小缺乏精度
    double dy=Function(x+dx)-Function(x);//函式值的增量
    double dd1=dy/dx;//導數
    double dd2=0;//dx變化時的導數
    dx=dx/2;//不斷地減少x的增量
    dy=Function(x)-Function(x+dx);
    dd2=dy/dx;//計算新的導數值
    while(abs(dd1-dd2)>eps)//當相鄰兩次的導數值小於精度時終止迭代,得到導數
    {
        dd1=dd2;
        dx=dx/2.0;
        dy=Function(x+dx)-Function(x);
        dd2=dy/dx;
    }
    return dd2;
}
//求函式的2階導數,與求一階導數的原理一樣,只需要把求函式值的函式Function換成求一階導數的函式Derivative
/*****************************\
輸入:x為自變數
輸出:x自變數對應的二階導數值
\*****************************/
double Derivative2(double x)
{
    double eps=0.00000001;
    double dx=0.5;
    double dy=Derivative(x+dx)-Derivative(x);
    double dd1=dy/dx;
    double dd2=0;
    dx=dx/2;
    dy=Derivative(x)-Derivative(x+dx);
    dd2=dy/dx;
    while(abs(dd1-dd2)>eps)
    {
        dd1=dd2;
        dx=dx/2.0;
        dy=Derivative(x+dx)-Derivative(x);
        dd2=dy/dx;
    }
    return dd2;
}
/********************函式的定義、一階導、二階導的模組  END*************************/
/******************************************\
輸入:a,b為區間的上下限,n為最大的迭代次數
輸出:列印函式最小值及對應的自變數x
\******************************************/
void GoldenSection(double a,double b,int n)//黃金分割法
{
    double l=a+0.382*(b-a);
    double h=a+0.618*(b-a);
    double region=b-a;
    
    double fl;
    double fh;
    int num=1;//迭代次數
    
    while(region>0.0000000001&&num<n)
    {
        fl=Function(l);
        fh=Function(h);
        if(fl>fh)
        {
            a=l;
            l=h;
            h=a+0.618*(b-a);
        }
        else
        {
            b=h;
            h=l;
            l=a+0.382*(b-a);
        }
        num++;
        region=abs(b-a);
    }
    if(num==n)
        printf("找不到最小值");
    else
    {
        printf("黃金分割法:x=%f時,最小值f(x)=%f",(a+b)/2,Function((a+b)/2));
        
    }
}
/******************************************\
輸入:a,b為區間的上下限
輸出:列印函式最小值及對應的自變數x
\******************************************/
void Dichotomy(double a,double b)//二分法
{
    double eps=0.0000001;
    double x=(a+b)/2;
    double region=b-a;
    double fxDerivative= Derivative(x);
    while(region>0.0000001&&abs(fxDerivative)>eps)
    {
        fxDerivative= Derivative(x);
        if(fxDerivative>eps)
            b=x;
        if(fxDerivative<-eps)
            a=x;
        x=(a+b)/2;
        region=abs(b-a);
    }
    printf("\n\n二分法:x=%f時,f(x)=%f\n",x,Function(x));
}
/******************************************\
輸入:a,b為區間的上下限,x1是初始值
輸出:列印函式最小值及對應的自變數x
\******************************************/
void Newton(double a,double b,double x1)
{
    double eps=0.0000001;
    double x=x1;
    double d1=Derivative(x1);//一階導
    double d2;//二階導
    while(abs(d1)>eps)
    {
        d2=Derivative2(x);
        if(d2<0)
            printf("二階導小於0,無法求解");
        else
        {
            x=x-d1/d2;//x迭代公式
            d1=Derivative(x);
        }
    }
    printf("\n牛頓法:x=%f時,f(x)=%f\n\n",x,Function(x));
}
void main()
{
    GoldenSection(0,1,100000);//黃金分割法
    
    Dichotomy(0,1);//二分法
    Newton(0,1,1);//牛頓法
}

執行結果如下圖:


作者:nineheadedbird