求 f(x) = 0.7*x + 8*sin(6*x) + 9*cos(3*x)
在区间[0,10]的最大值。要求结果保留到小数点后6位。
说实话我还真没做过这样的题目,要说最像的就是三分求函数的最值了,但是三分的前提是凸函数或者凹函数。
所以对于这道题问我一点办法都没有。
之后通过万能的百度,我查到了如何求任意一个函数的最值的方法。
///因为random库是C++11的拓展库,所以VC6跑不了 #include <cstring> #include <iostream> #include <cmath> #include <random> #include <chrono> #include <time.h> using namespace std; #define INF 0x3f3f3f3f int n,sx,sy; double Lbound, Ubound; double ansx; double ans=-INF,t; const double delta=0.993; mt19937 rng(chrono::steady_clock::now().time_since_epoch().count()); uniform_real_distribution<double> dis(-1,1); double func(double x) { return 0.7*x + 8*sin(6*x) + 9*cos(3*x); } void simulate_anneal() { double x=ansx; t = (Ubound - Lbound) + Lbound / 2.0; while (t>1e-14) { double X=x+dis(rng)*t; if (X>Ubound ||X <Lbound) continue; double now=func(X); double Delta=now-ans; if (Delta > 0) { x=X; ansx=x,ans=now; } else if (exp(-Delta/t)*1.0 > dis(rng)) x=X; t*=delta; } } inline void Solve() { ansx = (Ubound - Lbound) + Lbound / 2.0; for (int i = 0; i < 100; i++) simulate_anneal(); } int main() { Lbound = 0, Ubound = 10; time_t st = clock(); Solve(); cout << "Time spend: " << (double)(clock()-st) / ((clock_t)1000) << endl; printf("%.6f %.6f ",ansx,ans); return 0; }
这就算是好好学习了一下模拟退火吧。
不得不说,模拟退火的学问是真的很大。下面是我在学习模拟退火时看的几个博客,个人感觉都写的很好:
https://www.cnblogs.com/flashhu/p/8884132.html (PS:个人感觉这篇讲的最好,它让我清楚地知道了在模拟退火中的几个很重要的概念,真的很棒鸭)
https://www.cnblogs.com/rvalue/p/8678318.html
https://www.luogu.org/blog/m-sea/qian-tan-SA
谈一谈个人对模拟退火的理解。感觉就是x从中位数或者平均值开始,之后加上平均值*(-1,1)的随机浮点数。
带入式子,看是不是比当前全局最优解更优,如果更优的话就换掉,如果不更优的就以一定的几率更换X。
之后逐渐缩小随机函数跳跃间隔。最后就会在最优解附近了。(蒟蒻理解,大佬勿喷
当然最好多跑几遍,玄学算法嘛
最后在看大佬们的代码过程中,偷学到一点生成随机数的奇淫技巧。(我才不会告诉你们我之前还在用rand的
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
uniform_real_distribution<double> dis(-1,1);
运用这段代码需要#include <random>, #include <chrono>,并且使用c++11以上的版本的编译器
mt19937是一种生成随机数的类,mt19937 rng(chrono::steady_clock::now().time_since_epoch().count()); 这一句话就相当于生成一个叫rng的随机数
uniform_real_distribution<double> 是生成随机数的类?之后会生成一个可以定生成范围的函数?差不多这样的感觉。
所以uniform_real_distribution<double> dis(-1,1);就是生成了一个会生成-1~1的浮点数的函数。
double X=x+dis(rng)*t; 调用就差不多这么调用。