(SA)Simulated Annealing模擬退火算法

模擬退火算法是一種通用概率演算法,用來在一個大的搜索空間內找出最優解。相比于二分、三分等算法,模擬退火更加注意整體的情況,而非死磕在局部的變化。

爬山

可以拿爬山做例子:我們要找到山脈的最高峰,但是我們只知道眼前的點,哪邊是下降的,但看不到遠處的點是否上升。所以每次移動,我們隨機選擇一個方向。如果這個方向是上升的的(更優),那么就決定往那個方向走;如果這個方向是下降的(更差),那么“隨機地接受”這個方向,接受就走,不接受就再隨機一次。

模擬退火算法(流程)

  1. 隨機產生一個初始解x0,令xbest= x0 ,并計算目標函數值E(x0);
  2. 設置初始溫度T(0)=To,迭代次數i = 1;
  3. Do while T(i) > Tmin
    ① for j = 1~k
    ② 對當前最優解xbest按照某一鄰域函數,產生一新的解xnew。計算新的目標函數值E(xnew) ,并計算目標函數值的增量ΔE = E(xnew) - E(xbest) 。
    ③ 如果ΔE <0,則xbest = xnew;
    ④ 如果ΔE >0,則p = exp(- ΔE /T(i));
    (如果c = random[0,1] < p, xbest = xnew; 否則xbest = xbest)
    ⑤End for
  4. i = i + 1;
  5. End Do
  6. 輸出當前最優點,計算結束。


    Simulated Annealing

要注意的是,實際題目中exp(- ΔE /T(i))這個概率并不是必要情況,所以有時可以忽略(有點像貪心)
步驟:
①對于循環的判定可以設定為精度的判定,當步長STEP>EPS(EPS=1e-6)的時候執行循環,否則退出。
②在周圍搜索出新的一個點(注意判定這個點是否滿足規定范圍)
③判定這個點是否為最優點
如果是則更新當前點
如果不是就以一定概率更新當前點(可忽略)
④縮小步長STEP

一、對于1維坐標尋找Y的最小值,可以先規定一個起點(這里是原點),和每次前進/后退的長度(STEP)。每次按照這個STEP前進/后退,一旦發現更優的點就更新。并且每次都按一定的比例縮小前進/后退的距離(降低再次搜索的范圍)。當步數長度STEP小于規定精度時就可以退出搜索了。

const double EPS=1e-6;
const double r = 0.99;
const int dx[]= {-1,1};
…………………
double step=1;//規定初始步長長度
double x=0;//規定起始點
while(step > EPS)//設置精度范圍
{
        for(int i=0; i<2; i++)//分別前進和后退
        {
            double next_x = x+dx[i]*step;//確定下一個點的坐標x

            if(F(next_x)<F(x))//判斷是否為更優解
            {
                x=next_x;//更新坐標
            }
        }
        step*=r;//降低再次搜索的范圍 
}

例題:http://acm.hdu.edu.cn/showproblem.php?pid=2899
F(x) = 6 * x7+8*x6+7x^3+5x^2-y*x (0 <= x <=100)
Can you find the minimum value when x is between 0 and 100.
輸入Y值,求F(x)的最小值

#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<ctime>
using namespace std;
const double EPS=1e-6;
const double r = 0.99;
const int dx[]= {-1,1};
double y;

double F(double x)
{
    return 6*pow(x,7)+8*pow(x,6)+7*pow(x,3)+5*pow(x,2)-y*x;
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%lf", &y);
        double step=1;
        double x=0;
        while(step > EPS)
        {
            for(int i=0; i<2; i++)
            {
                double next_x = x+dx[i]*step;

                if(F(next_x)<F(x))
                {
                    x=next_x;
                }
            }
            step*=r;
        }
        printf("%.4lf\n", F(x));
    }
    return 0;
}

二、對于2維坐標尋找Z的最小值
同樣可以先規定一個起點(這里還是原點),和每次行進的長度(STEP)。每次按照這個STEP向東、南、西、北、東南、西南、東北、西北八個方向行進,一旦發現更優的點就更新。并且每次都按一定的比例縮小行進的距離(降低再次搜索的范圍)。當步數長度STEP小于規定精度時就可以退出搜索了。

const double EPS=1e-6;
const int dx[]= {-1,-1,-1,0,0,1,1,1};
const int dy[]= {-1,0,1,-1,1,-1,0,1};
const double r = 0.99;
……………………
double step=1.0;//規定初始步長長度
double x=0, y=0;//規定初始點
double z=F(x, y);
while(step > EPS)
{
   for(int i=0; i<8; i++)//向8個方向分別遍歷
   {
      double next_x = x + dx[i] * step;
      double next_y = y + dy[i] * step;
      double next_z = F(next_x, next_y)
      //這里還要判斷next_z是否滿足規定區域
      if(distance(next_x, next_y, next_z) < distance(x, y, z))//判斷更新最優解的條件
      {
          x = next_x;//滿足條件就更新坐標
          y = next_y;//滿足條件就更新坐標
          z = next_z;//滿足條件就更新坐標
       }
    }
    step*=r;//降低再次搜索的范圍 
}

例題:http://acm.hdu.edu.cn/showproblem.php?pid=5017
給定橢球公式,求從原點到橢球的最短距離

橢球公式

#include<cstdio>
#include<cmath>
using namespace std;
const double EPS=1e-6;
const double INF=0x3f3f3f3f;
const int dx[]= {-1,-1,-1,0,0,1,1,1};
const int dy[]= {-1,0,1,-1,1,-1,0,1};
const double r = 0.99;

double a, b, c, d, e, f, x, y, z;


double dis(double x, double y, double z)
{
    return sqrt(x*x+y*y+z*z);
}

double F(double x, double y)
{
    double aa, bb, cc;
    aa=c;
    bb=d*y+e*x;
    cc=a*x*x+b*y*y+f*x*y-1;
    if(bb*bb-4*aa*cc<0) return -INF;//點Z不在橢球面上
    if( dis(x,y,(sqrt(bb*bb-4*aa*cc)-bb))/(2*aa)>dis(x,y,(-sqrt(bb*bb-4*aa*cc)-bb))/(2*aa))//判斷一元二次方程到底取哪個解
        return (-sqrt(bb*bb-4*aa*cc)-bb)/(2*aa);
    else
        return (sqrt(bb*bb-4*aa*cc)-bb)/(2*aa);
}



int main()
{
    while(~scanf("%lf%lf%lf%lf%lf%lf", &a, &b, &c, &d, &e, &f))
    {
        double step=1.0;
        double x=0, y=0;
        double z=F(x, y);
        while(step > EPS)
        {
            for(int i=0; i<8; i++)
            {
                double next_x = x + dx[i] * step;
                double next_y = y + dy[i] * step;
                double next_z = F(next_x, next_y);

                if(next_z >= INF) continue;//點Z不在橢球面上

                if(dis(next_x, next_y, next_z) < dis(x, y, z))
                {
                    x = next_x;
                    y = next_y;
                    z = next_z;
                }
            }
            step*=r;
        }
        printf("%lf\n", dis(x, y, z));
    }
    return 0;
}

當然在一定情況下,上面的搜索并不能滿足要求,首先是對于初始點的位置,一個初始點可能會限定搜索的范圍,最后無法找出最優解。其次是方向的選擇,8個方向也并不是最優的方向。對于改進方式,就是在規定的區域內,隨機分布多個點,并且在每個點附近尋找最優解的時候,采取任意方向搜索。
三、對于二維規定區域找出滿足條件的最優解(坐標)
思路和上面大致相同,只是將原來的單點逐步搜索改為多點逐步搜索,搜索方向也變為任意方向搜索。

規定區域內找隨機分布的點
for(i=0; i<num; i++)
{
    m[i].x=(rand()%1000+1)/1000.0*x0;
    m[i].y=(rand()%1000+1)/1000.0*y0; 
}

初始步長的長度
double step=sqrt(x0*x0+y0*y0)/2;

任意方向的處理
double angle=(rand()%1000+1)/1000.0*2*PI;

還要注意判定每次新發現的點(最優解)是否在規定區域內部喔~

例題:http://acm.hdu.edu.cn/showproblem.php?pid=3932
題目大意:在平面中的已知點中,找到一個點A,這個點要求是到其他所有最長點的最短情況。(求出距離最遠的那個點到A的長度,并且這個長度是情況中最短的長度)輸出A的坐標和A到最遠點的距離。

#include<cstdio>
#include<ctime>
#include<cmath>
#include<algorithm>
using namespace std;
const double EPS=1e-6;
const double PI=acos(-1);
const double INF=0x3f3f3f3f;
const double r=0.8;

typedef struct st
{
    double x, y;
} ST;

ST a[10005];
ST m[10005];
int n;
double d[10005];

double dis(ST A, ST B)
{
    return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
}

double MAX(ST t)
{
    int i;
    double maxx=0;
    for(i=0; i<n; i++)
    {
        maxx=max(maxx,dis(t,a[i]));
    }
    return maxx;
}

int main()
{
    double x0, y0;
    srand(time(NULL));
    int init_num=15;
    while(~scanf("%lf%lf%d", &x0, &y0, &n))
    {
        int i, j;
        for(i=0; i<n ;i++)
        {
            scanf("%lf%lf", &a[i].x, &a[i].y);
        }

        for(i=0; i<init_num; i++)
        {
            m[i].x=(rand()%1000+1)/1000.0*x0;
            m[i].y=(rand()%1000+1)/1000.0*y0;
            d[i]=MAX(m[i]);
        }

        double step=sqrt(x0*x0+y0*y0)/2;
        ST next, temp;

        while(step>EPS)
        {
            for(i=0; i<init_num; i++)
            {
                temp.x=m[i].x;
                temp.y=m[i].y;
                for(j=0; j<50; j++)
                {
                    double angle=(rand()%1000+1)/1000.0*2*PI;
                    next.x=temp.x+cos(angle)*step;
                    next.y=temp.y+sin(angle)*step;

                    if(next.x<0 || next.x>x0 || next.y<0 || next.y>y0) continue;

                    if(MAX(next)<d[i])
                    {
                        m[i].x=next.x;
                        m[i].y=next.y;
                        d[i]=MAX(next);
                        //printf("ok\n");
                    }
                }
            }
            step*=r;
        }
        int flag;
        double ans=INF;
        for(i=0; i<init_num; i++)
            if(d[i]<ans)
            {
                ans=d[i];
                flag=i;
            }
        printf("(%.1lf,%.1lf).\n",m[flag].x,m[flag].y);
        printf("%.1lf\n",ans);
    }
    return 0;
}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,431評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,637評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,555評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,900評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,629評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,976評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,976評論 3 448
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,139評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,686評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,411評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,641評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,129評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,820評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,233評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,567評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,362評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,604評論 2 380

推薦閱讀更多精彩內容