工程优化学习(进退法、黄金分割法、二次插值法、三次插值法、最速下降法)
工程优化课程学了进退法、黄金分割法、二次插值法、三次插值法、最速下降法。我自己只测试用最速下降法求(恩,水货我只调试了这一个,调了好多次才调通,然后其他的也没心情调试了):minf(x) = x1^2 + 25*x2^2,得到结果如下图所示,与该函数的极小值点(0,0)在精度误差之内。
#ifndef ENGINEERINGOPTIMIZATION_H_INCLUDED #define ENGINEERINGOPTIMIZATION_H_INCLUDED #include <vector> using std::vector; /* description:区间类 */ class Region { public: double frontierLow; //一维定义域下界 double frontierHigh; //一维定义域上界 vector<double> lowerBoundDomain; //n维矢量下界 vector<double> upperBoundDomain; //n维矢量上界 Region() {} Region(double l, double h) { frontierLow = l; frontierHigh = h; } Region(double l, double h, vector<double> lowerBound, vector<double> upperBound) { frontierLow = l; frontierHigh = h; lowerBoundDomain = lowerBound; upperBoundDomain = upperBound; } Region(vector<double> lowerBound, vector<double> upperBound) { lowerBoundDomain = lowerBound; upperBound = upperBoundDomain; } Region(const Region ®ion) { frontierLow = region.frontierLow; frontierHigh = region.frontierHigh; lowerBoundDomain = region.lowerBoundDomain; upperBoundDomain = region.upperBoundDomain; } }; /* @description 计算n维矢量的模值 ** */ double vectorMag(const vector<double> &v); /* ** @description 进退法求搜索区间。 ** @param initialPoint 搜索初始点 ** @param initialStep 搜索初始步长 ** @param targetFunc 目标函数f(x)的回调函数,调用者需要提供此函数。 ** 它接受一个参数,变量x,返回目标函数的函数值 ** @return 返回搜索到的区间 */ //变量x是一维空间中的点 Region computeSearchRegion(double initialPoint, double initialStep, double (*targetFuc)(double x, int derivationOrder)); /* ** @description 黄金分割法求最小值,目标函数需是单峰函数。缩短搜素区间的三个原则(a<=x<=b): ** 1、“去坏留好”原则,若f(x1)<f(x2),则a<x_min<x2 ** 2、对称原则,x1-a = b-x2 ** 3、等比收缩原则,每次留下区间的长度都是上次留下区间长度的w倍(0<w<1) ** 调用者需提供目标函数 ** @param region 目标函数的定义域 ** @param precision 精度 ** @param target 指向目标函数的函数指针 ** @return 返回目标函数的极小值点 */ double goldenSectionMethod(const Region ®ion, double precision, double (*targetFuc)(double x, int derivationOrder)); /* @description 二次插值法 ** @return 返回目标函数的极小值点 */ double quadraticInterpolation(double x1, double x2, double x3, double (*targetFuc)(double x, int derivationOrder)); /* @description 三次插值法(Davidon搜索法) ** 目标函数的第二参数为求导阶数 ** @return 返回目标函数的极小值点 */ double cubicInterpolation(const Region ®ion, double step, double precision, double (*targetFuc)(double x, int derivationOrder)); /* @description 最速下降法求解极小值点 n维函数 ** @return 返回极小值点n维矢量 ** 回调函数 derivationOrder为负数时求最优步长,非负数求导 */ vector<double> steepestDescent(vector<double> &initialPoint, double precision, vector<double> (*targetFuc)(vector<double> &x, int derivationOrder)); #endif // ENGINEERINGOPTIMIZATION_H_INCLUDED
#include <iostream> #include "EngineeringOptimization.h" #include <cassert> #include <cmath> /* @description 计算n维矢量的模值 ** */ double vectorMag(const vector<double> &v) { double mag = 0; for (int i = 0; i < v.size(); i++) { mag += v[i] * v[i]; } return std::sqrt(mag); } /* ** @description 进退法求搜索区间。 ** @param initialPoint 搜索初始点 ** @param initialStep 搜索初始步长 ** @param double (*targetFunc)(double, double) 目标函数f(x)的回调函数,调用者需要提供此函数。 ** 它接受一个参数,变量x,返回目标函数的函数值 ** @return 返回搜索到的区间 */ Region computeSearchRegion(double initialPoint, double initialStep, double (*targetFuc)(double x, int derivationOrder)) { double frontierLow = initialPoint; double frontierHigh = initialPoint + initialStep; double fucValueLow = targetFuc(frontierLow, 0); double fucValueHigh = targetFuc(frontierHigh, 0); double step = initialStep; bool FLAG = false; if (fucValueHigh < fucValueLow) //前进运算 { do { if (FLAG) frontierLow -= step; step *= 2; frontierHigh += step; fucValueLow = fucValueHigh; fucValueHigh = targetFuc(frontierHigh, 0); FLAG = true; } while (fucValueHigh < fucValueLow); return Region(frontierLow, frontierHigh); } else //后退运算 { step = -(step / 4); do { if (FLAG) { frontierHigh = frontierLow - step; step += step; } frontierLow += step; fucValueHigh = fucValueLow; fucValueLow = targetFuc(frontierLow, 0); FLAG = true; } while (fucValueHigh >= fucValueLow); return Region(frontierLow, frontierHigh); } } /* ** @description 黄金分割法求最小值,目标函数需是单峰函数。缩短搜素区间的三个原则(a<=x<=b): ** 1、“去坏留好”原则,若f(x1)<f(x2),则a<x_min<x2 ** 2、对称原则,x1-a = b-x2 ** 3、等比收缩原则,每次留下区间的长度都是上次留下区间长度的w倍(0<w<1) ** 调用者需提供目标函数 ** @param region 目标函数的定义域 ** @param precision 精度 ** @param target 指向目标函数的函数指针 ** @return 返回目标函数的最小值 */ double goldenSectionMethod(const Region ®ion, double precision, double (*targetFuc)(double x, int derivationOrder)) { double frontierLow = region.frontierLow; double frontierHigh = region.frontierHigh; double x1 = frontierLow + 0.382 * (frontierHigh - frontierLow); double x2 = frontierLow + frontierHigh - x1; double valueLow = targetFuc(x1, 0); double valueHigh = targetFuc(x2, 0); while (valueLow > valueHigh) { frontierLow = x1; if (frontierHigh - frontierLow > -precision && frontierHigh - frontierLow < precision) return (frontierHigh + frontierLow) / 2.0; x1 = x2; x2 = frontierLow + 0.618 * (frontierHigh - frontierLow); valueLow = valueHigh; valueHigh = targetFuc(x2, 0); } while (valueLow <= valueHigh) { frontierHigh = x2; if (frontierHigh - frontierLow > -precision && frontierHigh - frontierLow < precision) return (frontierHigh + frontierLow) / 2.0; x2 = x1; x1 = frontierLow + 0.382 * (frontierHigh - frontierLow); valueHigh = valueLow; valueLow = targetFuc(x1, 0); } } /* @description 二次插值法 ** @return 返回目标函数的最小值 */ double quadraticInterpolation(double x1, double x2, double x3, double (*targetFuc)(double x, int derivationOrder)) { double tmp; if (x1 < x2) { tmp = x1; x1 = x2; x2 = tmp; } if (x3 < x2) { tmp = x2; x2 = x3; x3 = tmp; } double f1 = targetFuc(x1, 0); double f2 = targetFuc(x2, 0); double f3 = targetFuc(x3, 0); double c1 = (f3 - f1) / (x3 - x1); double c2 = ((f2 - f1) / (x2 - x1) - c1) / (x2 - x3); double x_min = 0.5 * (x1 + x3 - c1 / c2); return x_min; } /* @description 三次插值法(Davidon搜索法) ** 目标函数的第二参数为求导阶数 ** @return 返回目标函数最小值 */ double cubicInterpolation(const Region ®ion, double step, double precision, double (*targetFuc)(double x, int derivationOrder)) { double a = region.frontierLow; double b = region.frontierHigh; double h = step; bool FLAG = false; assert(precision > 0); double v = targetFuc(a, 1); if (v > -precision && v < precision) return a; do { if (FLAG) h /= 10; if (v < 0) h = std::abs(h); else h = -std::abs(h); double u; do { b = a + h; u = targetFuc(b, 1); if (u > - precision && u < precision) return b; if (u * v < 0) break; h *= 2; v =u; a = b; } while(true); double s = 3 * (targetFuc(a, 0) - targetFuc(b, 0)) / (b - a); double z = s - u -v; double w = std::sqrt(z * z - u * v); if (v > 0) { a = a - (b - a) * v / (z - w -v); } else { a = a - (b - a) * v / (z + w -v); } v = targetFuc(a, 1); FLAG = true; } while (v > precision || v < -precision); return a; } /* @description 最速下降法求解极小值点 n维函数 ** @return 返回极小值点n维矢量 ** 回调函数 derivationOrder为负数时求最优步长,非负数求导 */ vector<double> steepestDescent(vector<double> &initialPoint, double precision, vector<double> (*targetFuc)(vector<double> &x, int derivationOrder)) { vector<double> x_k; x_k = initialPoint; vector<double> x_k1; vector<double> s_k; double e = precision; do { s_k = targetFuc(x_k, 1); double mag = vectorMag(s_k); if (mag <= e) return x_k; vector<double> combine; for (int i = 0; i < x_k.size(); i++) combine.push_back(x_k.at(i)); for (int i = 0; i < s_k.size(); i++) combine.push_back(s_k.at(i)); vector<double> tmp = targetFuc(combine, -1); double step = vectorMag(tmp); std::cout << "step =" << step <<std::endl; x_k1.clear(); for (int i = 0; i < x_k.size(); i++) { x_k1.push_back(x_k.at(i) - s_k.at(i) * step); } x_k = x_k1; } while(true); }
#include <iostream> #include <vector> #include "EngineeringOptimization.h" using namespace std; vector<double> targetFuc(vector<double> &x, int derivationOrder); int main() { cout << "Hello world!" << endl; vector<double> x0; x0.push_back(2); x0.push_back(2); cout << "Intial Point: " << x0[0] << x0[1] <<endl; vector<double> min_value = steepestDescent(x0, 0.01, targetFuc); vector<double> result = targetFuc(min_value, 0); cout<< "x*= [" << min_value[0] <<" "<< min_value[1] <<"]"<<endl;; cout<<"The min value point is "<< result.at(0) <<endl;; return 0; } //min f(x)=x1^2 + 25x2^2 vector<double> targetFuc(vector<double> &x, int derivationOrder) { vector<double> f; double value; f.push_back(0); switch (derivationOrder) { case 0: f.clear(); value = x[0] * x[0] + 25 * x[1] * x[1]; f.push_back(value); break; case 1: f.clear(); value = 2 * x[0]; f.push_back(value); value = 50 * x[1]; f.push_back(value); break; default: break; } if (derivationOrder < 0) { f.clear(); value = (2.0*x[0]*x[2]+50*x[1]*x[3])/(2*x[2]*x[2] + 50*x[3]*x[3]); f.push_back(value); } return f; }