OSDN Git Service

Update hill-climbing.md
authorSiyuan <294873684@qq.com>
Wed, 29 Aug 2018 13:08:04 +0000 (21:08 +0800)
committerGitHub <noreply@github.com>
Wed, 29 Aug 2018 13:08:04 +0000 (21:08 +0800)
docs/misc/hill-climbing.md

index e69de29..2f6d3f6 100644 (file)
@@ -0,0 +1,60 @@
+## 简介 ##\r
+\r
+&emsp;&emsp;爬山算法是一种局部择优的方法,采用启发式方法,是对深度优先搜索的一种改进,它利用反馈信息帮助生成解的决策。\r
+\r
+---\r
+\r
+## 实现 ##\r
+\r
+&emsp;&emsp;爬山算法每次在当前找到的最优方案 $x$ 附近寻找一个新方案(一般随机差值)。如果这个新的解 $x'$ 更优,那么转移到 $x'$ 否则不变。\r
+\r
+&emsp;&emsp;这种算法对于单峰函数显然可行(你都知道是单峰函数了为什么不三分呢)。\r
+\r
+&emsp;&emsp;但是对于多数需要求解的函数中,爬山算法很容易进入一个局部最优解,如下图(最优解为 $\color{green}{\Uparrow}$,而爬山算法可能找到的最优解为 $\color{red}{\Downarrow}$)。\r
+\r
+![](https://s1.ax1x.com/2018/08/22/PooS9e.png)\r
+\r
+---\r
+\r
+## 代码 ##\r
+\r
+&emsp;&emsp;此处代码以 [「BZOJ 3680」吊打XXX](https://www.lydsy.com/JudgeOnline/problem.php?id=3680)(求 $n$ 个点的带权类费马点)为例。\r
+\r
+```cpp\r
+#include <cstdio>\r
+#include <cmath>\r
+const int N=10005;\r
+int n,x[N],y[N],w[N];\r
+double ansx,ansy;\r
+void hillclimb() {\r
+       double t=1000;\r
+       while(t>1e-8) {\r
+               double nowx=0,nowy=0;\r
+               for(int i=1;i<=n;++i) {\r
+                       double dx=x[i]-ansx,dy=y[i]-ansy;\r
+                       double dis=sqrt(dx*dx+dy*dy);\r
+                       nowx+=(x[i]-ansx)*w[i]/dis;\r
+                       nowy+=(y[i]-ansy)*w[i]/dis;\r
+               }\r
+               ansx+=nowx*t,ansy+=nowy*t;\r
+               if(t>0.5) t*=0.5; else t*=0.97;\r
+       }\r
+}\r
+int main() {\r
+       scanf("%d",&n);\r
+       for(int i=1;i<=n;++i) {\r
+               scanf("%d%d%d",&x[i],&y[i],&w[i]);\r
+               ansx+=x[i],ansy+=y[i];\r
+       }\r
+       ansx/=n,ansy/=n;\r
+       hillclimb();\r
+       printf("%.3lf %.3lf\n",ansx,ansy);\r
+       return 0;\r
+}\r
+```\r
+\r
+---\r
+\r
+## 劣势 ##\r
+\r
+&emsp;&emsp;其实爬山算法的劣势上文已经提及:它容易陷入一个局部最优解。当目标函数不是单峰函数时,这个劣势是致命的。因此我们要引进 **模拟退火**,此算法详见:[模拟退火 - OI Wiki](https://oi-wiki.org/misc/simulated-annealing/)。\r