Loading

C艹老师布置的思考题

 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\n",ansx,ans);
    return 0;
}
View Code

这就算是好好学习了一下模拟退火吧。

不得不说,模拟退火的学问是真的很大。下面是我在学习模拟退火时看的几个博客,个人感觉都写的很好:

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; 调用就差不多这么调用。

 



posted @ 2019-10-30 18:19  ViKyanite  阅读(496)  评论(0编辑  收藏  举报