POJ2069 最小球覆盖 模拟退火算法

     之前对模拟退火一直没能较好的理解,今天借别人的代码分析了一波。

     https://www.cnblogs.com/lfri/p/11604701.html 这篇博客的第二份代码就是这题模拟退火的解法。有了自己的理解。

     这份代码的思路实际上是先通过求平均找到一个起始点P0。然后将这个P0不断蹦来蹦去找到距离答案最近的点。它这个蹦来蹦去不是乱蹦,是利用了退火函数的优点来蹦。

     先对一个比较简单的情况分析,假如问题是:一个单调函数(区间[a,b]),现在让你找出最大值时的x的值,如下图

     答案显而易见是b,那么利用模拟退火是怎么找到答案的呢。

     这次还是先说解法,再理解这个解法。首先 随便找一个初始点P0(P0属于[a,b]),那么下一步P0往左蹦和往右蹦的概率相等(通过Rand()*2.0-1 就可以实现)。

    现在加一个限制,假如下一个随机的点的函数值f(next)大于当前函数值f(cur)就一定蹦(那么只要随机的是右边,一定会蹦过去);

    如果小于当前函数值f(cur)就给它一个概率选择蹦或不蹦(这里限制的是再随机一个值rand1,如果

rand1<e^{-(f(cur)-f(next))/T} 就蹦)。根据下图1可以发现,如果我们(f(cur)-f(next))大于0,那么整个函数是随T的增加而增加的。对了,这里的T就是模拟退火中的温度值,T的初始值为b-a比较合适,每次退火T*=0.9999,就是说T在退火过程中不断减少。

    那么我们先看T不变时(即讨论当前选择随机点时蹦的概率),可以发现(f(cur)-f(next))越大,蹦的概率越小(下图2),换言之,越往左概率越低。

    现在从整体角度去看,你从a+delta*0点开始蹦(delta是大于0的无穷小),你从a+delta*1点开始蹦,你从a+delta*2点开始蹦,你从a+delta*3点开始蹦,你从a+delta*4开始点蹦,你从a+delta*5点开始蹦,......,你从b开始蹦。把所有这些点的贡献值加起来,会发现蹦到b的概率最高,蹦到a的概率最低。

     而且,随着温度T的降低,每次蹦的步长会越来越小。

     现在可以为这条线脑补一个概率(会蹦到某点的概率)分布,可以感觉到最高点的概率最大,最低点概率最小。也就是说,假设你现在给的时间复杂度为1e6级别,我把1e6个检测点按照这个概率分布撒在这条线上,最高点附近撒的点会很多,最低点就很少。然后在你撒的点中找到最大的值,自然结果就很接近正确答案。


 

     然后将问题引申到更复杂的情况,如下图。还是和之前一样分析,会发现高处概率高,低处概率低。

     最后总结一下,这种方法适合于连续函数,不适合于分段函数或在某段极小(相对于你撒点的密度)的区间内有极大的变化(如下图情况),因为你仍然用你原来的撒点密度可能就忽略了这个及其细小的高峰,导致该处的概率和附近的低谷差不多,该处撒的点就少了,可能就撒不到这个细区间内,就更新不了该处的答案。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章