爬山算法是一种局部搜索。它从一个现成的解出发,每次只看周围的解,哪一个更好就走过去,直到周围都不如当前这一点,就停下来。
「更好」由目标函数决定。要爬到最高,就选函数值更大的邻居;要找最低成本,就选函数值更小的邻居。每一步只往更优的方向走,所以叫爬山。
用一条高度线看这个过程。横轴是位置,纵轴是高度,目标是站到最高处。
| 位置 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|---|
高度 | 1 | 3 | 2 | 5 | 4 | 1 | 6 |
邻居就是左右相邻的一格。
从位置 1 出发,高度是 3。左边高度 1,右边高度 2,都更低,算法停在这里。这是一个局部最高点,整条线的最高点在位置 6,高度是 6,它走不到。
从位置 4 出发,高度是 4。左边位置 3 的高度是 5,比 4 高,于是走到 3。到了 3 以后,左边 2、右边 4 都更低,又停住。这次停在高度 5,仍然不是全局最高。
从位置 5 出发,高度是 1。右边位置 6 的高度是 6,走到 6 后两边都没有更高的格子,停在全局最高点。
同一个算法,起点不同,停下来的地方就不同。它只保证停在一个「周围都更差」的点,不保证这是整张图里最好的点。
每一步如果把所有邻居都看完,再挑最好的那个,叫最陡上升。邻居很多时,也可以碰到第一个更好的就走,不必把全部邻居算完。为了不总停在同一个局部高点,可以从多个随机起点各爬一次,留下其中最高的结果。
配送路线、排课、参数调节都可以这样用:当前方案改一小步(对调两个城市、挪一节课),总代价变好就接受,变差就保持原样,直到小改都不再变好。
和退火算法的区别
两者都是从当前解走到邻居,差别在于变差的那一步收不收。
爬山只在邻居更好时才移动。周围都更差就停住,所以会停在局部最高点。用前面那条高度线,从位置 1(高度 3)出发,左右是 1 和 2,都更低,爬山停在 3,到不了位置 6 的高度 6。
模拟退火在邻居更差时仍按概率接受。差得越多、温度越低,接受概率越小:
P=e^(−Δ/T)
从高度 3 走到高度 2,Δ=1。温度 T=2 时,P=e−1/2≈0.61,这一步有大约六成机会被留下。留下之后才有机会继续走到后面的全局最高点。温度再降下去,差步越来越难被接受,搜索逐渐稳定。
所以爬山的结果由起点和邻居决定,同一起点反复跑,停的地方相同。退火每次跑都可能走不同的路,用降温换一次跳出局部高点的机会。