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;
}
}