如何实现一个高效的启发式算法?

一、前言

小伙伴们好,说起来已经好久好久好久没见了呢!之前一直忙着做其他事情去了(泛指学习一类),公众号已经落下好久好久了。今天来写点好玩的东西。

说起来,小编似乎就是做启发式算法起家的。当时记得老师是这么跟我说的,启发式算法这东西很简单,你不需要基础,有高中基础就够了(其实他想说的是初中……)。后来小编一直在学这个东西,做了三四年了,用启发式算法做过的大大小小的project已经不记得有多少了,所以还算得上有一点点经验。

因此今天就来写写,怎样实现一个比较高效的启发式算法吧~

二、何为高效?

说到这个词,相信大家一定不陌生。高效意思就是达到相同效果或者更好的效果时,使用的时间更短,所需要的资源更少。就拿小编来说,由于小编特别笨,学一样东西需要花一周的时间,而群里的小伙伴只需要一天的时间就能学会。那么这位小伙伴是要比我高效的。

同样的对于一个启发式算法而言,不同人实现出来,即使是使用同一编程平台达到同样的效果,运行时间也会千差万别,相差几倍甚至几十倍。这样说出来大家可能还没啥感觉,那么放一下我们之前做过的一些数值实验大家直观感受下吧~

这是某个Java实现的求解VRP类问题的算法代码,两个算法都达到了同样的效果,只不过绿色曲线对应的算法在计算过程中去除了相关冗余,可以看到运行时间直线下降。根据我们的统计,消除冗余计算后可使计算时间降低约83%,对于工业化生产而言,提升哪怕一个小数点都能带来巨大的收益,何止是83个百分点呢。怎么说呢,这简直是A small step forward,one step civiliazation

三、放码过来?

不要一上来就是写代码,不加思考就上手写代码,你只会搓一坨屎山出来,自己坑害自己。开始写代码之前一定要构思好算法的整体架构,解的表示方式,如何快速得到邻居解等。建议是思考的时间一定要占总时间的一半以上。

其实思路清晰写代码是非常快的,比如每次在写代码的时候我都会先写好注释,比如:

//1. 先获取所有可行点的信息

//2. 对点按照成本进行排序

//3. 贪心将各个点插入到解中

然后写的时候我只需要按照这个思路往下走就可以了,这就跟你写小学生作文一样,起床刷牙到公园看鲸鱼,一定要思路清晰。

四、邻居解如何计算?

到了今天的核心问题,我们都知道,邻域搜索过程中,邻居解与当前解相比往往只有细微的变化,因此迭代过程中绝大部分变量不需要重新计算,消除了冗余计算,可大大提高邻域搜索效率,降低运行时间。听不懂吗?没关系,我举例子,慢慢给你讲解。

下面是一个VRP问题(没有TW哦)的一个初始解

S_1
:

为了方便表示我们用

C(x)
表示x的cost吧,其中x可以为一个解、解中的一条路径。
c_{ij}
表示边<i,j>的距离。对于初始解,计算它的cost,只能是从头到尾挨个遍历一下了。因此:

C(S_1) = C(r_1) + C(r_2) + C(r_3) + C(r_4)

其中各条路径的cost又可以表示为:

C(r_1) = c_{0,6} +c_{6,4}+ ... + c_{8,0} \\ ......\\ C(r_4) = c_{0,14} +c_{14,18}+ ... + c_{25,0}

因此最后解的计算方式为:

C(S_1) = c_{0,6} +c_{6,4}+ ... +c_{25,0}

为了方便比较我们将这种计算解cost的方式称为Algorithm1

算一下,Algorithm1在计算时遍历了所有的客户点,对于N个客户点的解而已,算法的复杂度为O(N),嗯!还不赖。

好了现在解

S_1
通过一个move,生成了一个邻居解
S_2

image

可以看到该move就是将客户19从路径

r_2
中移除,并重新relocate到路径
r_3
中客户1后面。

现在生成了一个邻居解,得知道这个邻居解是好还是坏对吧,那么我们得比较

C(S_1)
C(S_2)
的大小吧。前面我们通过Algorithm1算出了
C(S_1)
的大小,那么现在的问题就是
C(S_2)
怎么计算了。敲黑板的重点来了。

利用Algorithm1

S_2
重新进行计算,时间复杂度前面分析过了,为
O(n)

优化一下

观察上面的解

S_1
S_2
,可以发现一个邻域搜索算法非常显著的特点:邻居解
S_2
相比较当前解
S_1
而言,只发生了微小的改变,整个解中有4条路径,只有两条路径发生了改变,因此
S_2
的cost可以由原来计算好的一些结果进行换算:

C(S_2) = C(S_1) - C(r_2) - C(r_3) + C(r'_2) + C(r'_3)

在上面的式子中,只有

C(r'_2)
C(r'_3)
是需要重新算的:
C(r'_2) = c_{0,15} +c_{15,12}+ ... + c_{7,0}\\ C(r'_3) = c_{0,32} +c_{32,13}+ ... + c_{5,0}

这样一来,只需要计算变动的路径即可,就不用重新计算所有路径了。大大降低了邻居解的计算时间复杂度。

进一步优化

细细观察一下

r'_2
r'_3
我们发现,路径中发生变动的边似乎也不是很多啊。我给大家标一下:

image

S_1
中发生变动的边我已经用红色实线标出来了,
S_2
中发生变动的边我用红色虚线标出来,那么
C(r'_2)
C(r'_3)
是不是可以用之前计算好的
C(r_2)
C(r_3)
得出来呢?当然可以啦,我们只需减去原来的边,再加上新的边就可以了。

C(r'_2) = C(r_2) - c_{11,19}-c_{19,17}+c_{11,17}\\ C(r'_3) = C(r_3) - c_{1,2}+c_{1,19}+c_{19, 2}

我们将这两条式子带入下面的式子:

C(S_2) = C(S_1) - C(r_2) - C(r_3) + C(r'_2) + C(r'_3)

即可得到:

C(S_2) = C(S_1) - c_{11,19}-c_{19,17}+c_{11,17} - c_{1,2}+c_{1,19}+c_{19, 2}

这个时间复杂度为

O(1)
,OK。经过重重优化,将时间复杂度从原来的
O(n)
成功降到了
O(1)

可能大家对这个

O(n)
降到
O(1)
没什么感觉。我来给大家分析一下,在小规模问题,比如10个、20个节点这样可能还真没啥区别。但是别忘记了启发式算法是针对大规模的优化问题的,邻域搜索类算法的邻域规模往往是随着问题规模的增长而呈爆炸式增长的。比如说在一个100个节点的VRP算例中,对于exchange算子,交换任意两个客户,那么一个解所能形成的邻居解就有
C_{100}^{2}=4950
,大约为5000个邻居解。

  • 如果每个邻居解你都使用Algorithm1重新算一遍,那么大概要算5000*100=50万次。
  • 如果你用优化过的计算方法,只需要算5000*1=5000次。

这个差距有多大,大家动动屁股都知道了。当然了,这只是理论上的分析。实际上的差距和编程环境以及实现方式等都有很大关系,但是只要差距超过10倍以上,都是能很明显的感觉出来的。

五、小结

关于如果去除冗余,不是说一套方法通用的,需要根据算法工程师根据问题的特性,设计合适的解结构,再对邻域算子进行降冗余的思考与实现。降冗余的操作比较适合邻域搜索类的启发式算法,因为这类算法显著特点就是邻居解相比较当前解而言,变化非常细微。

当然了,这需要丰富的编程经验,所以大家有时间还是好好磨练下吧,相应的学习代码请点击头像获取哦。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342