结合0.618法的最速下降法

1、先表示出目标函数f1()、f2(),以及进行自动求导的函数_deta(),并得到最速下降方向d(x)=- _deta(x);

//此处由题意,自变量X为二维;
//题目1的目标函数f(x)
double f1(double* x) {
    return x[0] * x[0] - 2 * x[0] * x[1] + 4 * x[1] * x[1] + x[0] - 3 * x[1];
}
//题目2的目标函数f(x) 
double f2(double* x) {
    return 2 * sin(x[0]) * sin(x[1]);
}
//该函数实现自动求导(定义法),并输出最速下降方向d = -deta。
double* _deta(double* x, double f(double*)){
    double* res = new double[2];
    double* _x = new double[2];
    _x[0] = x[0] + h; _x[1] = x[1];
    res[0] = -(f(_x) - f(x)) / h;//
    _x[0] = x[0]; _x[1] = x[1] + h;
    res[1] = -(f(_x) - f(x)) / h;//

    return res;
}

2、求向量的范数,后续用于判断是否达到精度要求。

//求向量的范数;
double norm(double* dir) {
    //参数 dir 即传入的向量,求其欧氏范数;
    return sqrt(dir[0] * dir[0] + dir[1] * dir[1]);
}

3、0.618法

//0.618法,golden_section(黄金分割)法,用于一维搜索;
double* golden_section(double* x, double f(double*)) {
    //参数 x 是初点,即x0;
    //参数 f 是目标函数;
    //参数 d 是最速下降方向,即-f'(x);
    double* a = new double[2]; double* b = new double[2]; double* dir = new double[2];
    //确定初始的a,b值,a=x0, b = x0 + 1 * d; 
    for (int i = 0; i < 2; ++i) {
        a[i] = x[i];
        b[i] = x[i] + _deta(x, f)[i];
    }
    for (int i = 0; i < 2; ++i) {
        dir[i] = b[i] - a[i];//区间长度;
    }
    double* landa = new double[2]; double* mu = new double[2];
    //设置landa,mu的值;
    for (int i = 0; i < 2; ++i) {
        landa[i] = a[i] + 0.382 * (b[i] - a[i]);
        mu[i] = a[i] + 0.618 * (b[i] - a[i]);
    }

    while (true) {
        if (norm(dir) < L) break;//区间长度足够短,即达到精度要求,退出循环;
        if (f(landa) > f(mu)) {//f(landa) > f(mu);
            for (int i = 0; i < 2; ++i) {
                a[i] = landa[i];
                landa[i] = mu[i];
                mu[i] = a[i] + 0.618 * (b[i] - a[i]);//a=landa, landa=mu,mu = a+0.618*(b-a);
            }
        }
        else {//f(landa) <= f(mu);
            for (int i = 0; i < 2; ++i) {
                b[i] = mu[i];
                mu[i] = landa[i];
                landa[i] = a[i] + 0.382 * (b[i] - a[i]);//b=mu,mu=landa,landa = a+0.382*(b-a);
            }
        }
        for (int i = 0; i < 2; ++i) {
            dir[i] = b[i] - a[i];//更新区间长度;
        }
    }

    return a;//返回得到的解;
}

4、最速下降法

//最速下降法; 沿着最速下降方向,得到最优解;
void Fast_Down(double* x, double f(double*)) {
    //参数 x 为初点;
    //参数 f 为目标函数;
    //参数 d 为-f'(x);
    cout << "初点x = [" << x[0] << ", " << x[1] << "]" << endl;//输出 初始点;
    double* direction = _deta(x, f);//在初点的最速下降方向;

    int times = 1;//记录迭代次数;
    while (true) {
        if (norm(direction) <= Epsilon ) break;//达到精度要求,则得到最优解,退出循环;
        //cout << "第" << times << "次迭代,d的范数:||d|| = " << norm(direction) << endl;//输出范数;

        double* landa = golden_section(x, f);//得到根据最速下降方向,进行一维搜索后的得到的点;
        //cout << "根据0.618法得到的步长:" << (x[0] - landa[0]) / direction[0] << endl;//输出步长;
        cout << "此时x(" << times << ") = [ ";//输出 第i次循环得到的解;
        for (int i = 0; i < 2; ++i) {
            x[i] = landa[i];//把由一维搜索得到的点,赋值给x;
            if (i == 1) cout << x[i];
            else cout << x[i] << "   ";
        }
        cout << "]";
        cout << endl << "目标函数值f(x) = " << f(x) << endl << endl;//输出此次循环的目标函数值;
        direction = _deta(x,f);
        times++;
    }
    //cout << "d的范数: || d || = " << norm(direction) << endl;//输出范数;
    cout <<"已经达到精度要求!!!" << endl;//输出达到精度要求的范数;
    cout << "最优解x = [" << x[0] << ", " << x[1] << "]" << endl;//输出最优解;
    cout <<  "最优值f(x) = " << f(x) << endl << endl;//输出最优值;
}

完成上述内容后,编写主函数,针对所求的问题,调用上述函数即可:

#include <iostream>
using namespace std;
#define L 1e-9
#define Epsilon 1e-6
#define h 1e-10//用于计算导数。
int main()
{
    char num = ' ';
    cout << "在第一题中,你想完成第几小题?(1或2):";
    cin >> num;
    double* x = new double[2]; x[0] = 1, x[1] = 1;
    if (num == '1') {
        x[0] = 1, x[1] = 1;
        Fast_Down(x, f1);
    }   
    else if (num == '2') {
        x[0] = 4.8, x[1] = 1.6;;
        Fast_Down(x, f2);
    }
    else {
        cout << "Error!请正确输入1或2." << endl;
    }
}

完整的程序代码如下:

#include <iostream>
using namespace std;
#define L 1e-9
#define Epsilon 1e-6
#define h 1e-10//用于计算导数。
//题目1的目标函数f(x)
double f1(double* x) {
    return x[0] * x[0] - 2 * x[0] * x[1] + 4 * x[1] * x[1] + x[0] - 3 * x[1];
}
//题目2的目标函数f(x) 
double f2(double* x) {
    return 2 * sin(x[0]) * sin(x[1]);
}

//该函数实现自动求导(定义法),并输出最速下降方向d = -deta。
double* _deta(double* x, double f(double*)){
    double* res = new double[2];
    double* _x = new double[2];
    _x[0] = x[0] + h; _x[1] = x[1];
    res[0] = -(f(_x) - f(x)) / h;//
    _x[0] = x[0]; _x[1] = x[1] + h;
    res[1] = -(f(_x) - f(x)) / h;//

    return res;
}
//求向量的范数;
double norm(double* dir) {
    //参数 dir 即传入的向量,求其欧氏范数;
    return sqrt(dir[0] * dir[0] + dir[1] * dir[1]);
}
//0.618法,golden_section(黄金分割)法,用于一维搜索;
double* golden_section(double* x, double f(double*)) {
    //参数 x 是初点,即x0;
    //参数 f 是目标函数;
    //参数 d 是最速下降方向,即-f'(x);
    double* a = new double[2]; double* b = new double[2]; double* dir = new double[2];
    //确定初始的a,b值,a=x0, b = x0 + 1 * d; 
    for (int i = 0; i < 2; ++i) {
        a[i] = x[i];
        b[i] = x[i] + _deta(x, f)[i];
    }
    for (int i = 0; i < 2; ++i) {
        dir[i] = b[i] - a[i];//区间长度;
    }
    double* landa = new double[2]; double* mu = new double[2];
    //设置landa,mu的值;
    for (int i = 0; i < 2; ++i) {
        landa[i] = a[i] + 0.382 * (b[i] - a[i]);
        mu[i] = a[i] + 0.618 * (b[i] - a[i]);
    }

    while (true) {
        if (norm(dir) < L) break;//区间长度足够短,即达到精度要求,退出循环;
        if (f(landa) > f(mu)) {//f(landa) > f(mu);
            for (int i = 0; i < 2; ++i) {
                a[i] = landa[i];
                landa[i] = mu[i];
                mu[i] = a[i] + 0.618 * (b[i] - a[i]);//a=landa, landa=mu,mu = a+0.618*(b-a);
            }
        }
        else {//f(landa) <= f(mu);
            for (int i = 0; i < 2; ++i) {
                b[i] = mu[i];
                mu[i] = landa[i];
                landa[i] = a[i] + 0.382 * (b[i] - a[i]);//b=mu,mu=landa,landa = a+0.382*(b-a);
            }
        }
        for (int i = 0; i < 2; ++i) {
            dir[i] = b[i] - a[i];//更新区间长度;
        }
    }

    return a;//返回得到的解;
}
//最速下降法; 沿着最速下降方向,得到最优解;
void Fast_Down(double* x, double f(double*)) {
    //参数 x 为初点;
    //参数 f 为目标函数;
    //参数 d 为-f'(x);
    cout << "初点x = [" << x[0] << ", " << x[1] << "]" << endl;//输出 初始点;
    double* direction = _deta(x, f);//在初点的最速下降方向;

    int times = 1;//记录迭代次数;
    while (true) {
        if (norm(direction) <= Epsilon ) break;//达到精度要求,则得到最优解,退出循环;
        //cout << "第" << times << "次迭代,d的范数:||d|| = " << norm(direction) << endl;//输出范数;

        double* landa = golden_section(x, f);//得到根据最速下降方向,进行一维搜索后的得到的点;
        //cout << "根据0.618法得到的步长:" << (x[0] - landa[0]) / direction[0] << endl;//输出步长;
        cout << "此时x(" << times << ") = [ ";//输出 第i次循环得到的解;
        for (int i = 0; i < 2; ++i) {
            x[i] = landa[i];//把由一维搜索得到的点,赋值给x;
            if (i == 1) cout << x[i];
            else cout << x[i] << "   ";
        }
        cout << "]";
        cout << endl << "目标函数值f(x) = " << f(x) << endl << endl;//输出此次循环的目标函数值;
        direction = _deta(x,f);
        times++;
    }
    //cout << "d的范数: || d || = " << norm(direction) << endl;//输出范数;
    cout <<"已经达到精度要求!!!" << endl;//输出达到精度要求的范数;
    cout << "最优解x = [" << x[0] << ", " << x[1] << "]" << endl;//输出最优解;
    cout <<  "最优值f(x) = " << f(x) << endl << endl;//输出最优值;
}
int main()
{
    char num = ' ';
    cout << "在第一题中,你想完成第几小题?(1或2):";
    cin >> num;
    double* x = new double[2]; x[0] = 1, x[1] = 1;
    if (num == '1') {
        x[0] = 1, x[1] = 1;
        Fast_Down(x, f1);
    }   
    else if (num == '2') {
        x[0] = 4.8, x[1] = 1.6;;
        Fast_Down(x, f2);
    }
    else {
        cout << "Error!请正确输入1或2." << endl;
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值