如何實現一個高效的啓發式算法?

1、前言

小夥伴們好,提及來已經很久很久很久沒見了呢!以前一直忙着作其餘事情去了(泛指學習一類),公衆號已經落下很久很久了。今天來寫點好玩的東西。node

提及來,小編彷佛就是作啓發式算法起家的。當時記得老師是這麼跟我說的,啓發式算法這東西很簡單,你不須要基礎,有高中基礎就夠了(其實他想說的是初中……)。web

後來小編一直在學這個東西,作了三四年了,用啓發式算法作過的大大小小的project已經不記得有多少了,因此還算得上有一點點經驗。所以今天就來寫寫,怎樣實現一個比較高效的啓發式算法吧~算法

2、何爲高效?

說到這個詞,相信你們必定不陌生。高效意思就是達到相同效果或者更好的效果時,使用的時間更短,所須要的資源更少。就拿小編來講,因爲小編特別笨,學同樣東西須要花一週的時間,而羣裏的小夥伴只須要一天的時間就能學會。那麼這位小夥伴是要比我高效的。編程

一樣的對於一個啓發式算法而言,不一樣人實現出來,即便是使用同一編程平臺達到一樣的效果,運行時間也會千差萬別,相差幾倍甚至幾十倍。這樣說出來你們可能還沒啥感受,那麼放一下咱們以前作過的一些數值實驗你們直觀感覺下吧~微信

這是某個Java實現的求解VRP類問題的算法代碼,兩個算法都達到了一樣的效果,只不過綠色曲線對應的算法在計算過程當中去除了相關冗餘,能夠看到運行時間直線降低。根據咱們的統計,消除冗餘計算後可以使計算時間下降約83%,對於工業化生產而言,提高哪怕一個小數點都能帶來巨大的收益,何止是83個百分點呢。怎麼說呢,這簡直是A small step forward,one step civiliazation架構


3、放碼過來?

不要一上來就是寫代碼,不加思考就上手寫代碼,你只會搓一坨屎山出來,本身坑害本身。開始寫代碼以前必定要構思好算法的總體架構,解的表示方式,如何快速獲得鄰居解等。建議是思考的時間必定要佔總時間的一半以上。app

其實思路清晰寫代碼是很是快的,好比每次在寫代碼的時候我都會先寫好註釋,好比:編輯器

//1. 先獲取全部可行點的信息

//2. 對點按照成本進行排序

//3. 貪心將各個點插入到解中

而後寫的時候我只須要按照這個思路往下走就能夠了,這就跟你寫小學生做文同樣,起牀刷牙到公園看鯨魚,必定要思路清晰。svg

4、鄰居解如何計算?

到了今天的核心問題,咱們都知道,鄰域搜索過程當中,鄰居解與當前解相比每每只有細微的變化,所以迭代過程當中絕大部分變量不須要從新計算,消除了冗餘計算,可大大提升鄰域搜索效率,下降運行時間。聽不懂嗎?不要緊,我舉例子,慢慢給你講解。學習

下面是一個VRP問題(沒有TW哦)的一個初始解 :

爲了方便表示咱們用 表示x的cost吧,其中x能夠爲一個解、解中的一條路徑。 表示邊<i,j>的距離。對於初始解,計算它的cost,只能是從頭至尾挨個遍歷一下了。所以:

其中各條路徑的cost又能夠表示爲:

所以最後解的計算方式爲:

爲了方便比較咱們將這種計算解cost的方式稱爲Algorithm1

算一下,Algorithm1在計算時遍歷了全部的客戶點,對於N個客戶點的解而已,算法的複雜度爲O(N),嗯!還不賴。

好了如今解 經過一個move,生成了一個鄰居解

能夠看到該move就是將客戶19從路徑 中移除,並從新relocate到路徑 中客戶1後面。

如今生成了一個鄰居解,得知道這個鄰居解是好仍是壞對吧,那麼咱們得比較 的大小吧。前面咱們經過Algorithm1算出了 的大小,那麼如今的問題就是 怎麼計算了。敲黑板的重點來了。

利用Algorithm1 從新進行計算,時間複雜度前面分析過了,爲

優化一下

觀察上面的解 ,能夠發現一個鄰域搜索算法很是顯著的特色:鄰居解 相比較當前解 而言,只發生了微小的改變,整個解中有4條路徑,只有兩條路徑發生了改變,所以 的cost能夠由原來計算好的一些結果進行換算:

在上面的式子中,只有 是須要從新算的:

這樣一來,只須要計算變更的路徑便可,就不用從新計算全部路徑了。大大下降了鄰居解的計算時間複雜度。

進一步優化

細細觀察一下 咱們發現,路徑中發生變更的邊彷佛也不是不少啊。我給你們標一下:


中發生變更的邊我已經用紅色實線標出來了, 中發生變更的邊我用紅色虛線標出來,那麼 是否是能夠用以前計算好的 得出來呢?固然能夠啦,咱們只需減去原來的邊,再加上新的邊就能夠了。

咱們將這兩條式子帶入下面的式子:

便可獲得:

這個時間複雜度爲 ,OK。通過重重優化,將時間複雜度從原來的 成功降到了

可能你們對這個 降到 沒什麼感受。我來給你們分析一下,在小規模問題,好比10個、20個節點這樣可能還真沒啥區別。可是別忘記了啓發式算法是針對大規模的優化問題的,鄰域搜索類算法的鄰域規模每每是隨着問題規模的增加而呈爆炸式增加的。好比說在一個100個節點的VRP算例中,對於exchange算子,交換任意兩個客戶,那麼一個解所能造成的鄰居解就有 ,大約爲5000個鄰居解。

  • 若是每一個鄰居解你都使用 Algorithm1從新算一遍,那麼大概要算5000*100=500000次。
  • 若是你用優化過的計算方法,只須要算5000*1=5000次。

這個差距有多大,你們動動屁股都知道了。固然了,這只是理論上的分析。實際上的差距和編程環境以及實現方式等都有很大關係,可是隻要差距超過10倍以上,都是能很明顯的感受出來的。

5、小結

關於若是去除冗餘,不是說一套方法通用的,須要根據算法工程師根據問題的特性,設計合適的解結構,再對鄰域算子進行降冗餘的思考與實現。降冗餘的操做比較適合鄰域搜索類的啓發式算法,由於這類算法顯著特色就是鄰居解相比較當前解而言,變化很是細微。

固然了,這須要豐富的編程經驗,因此你們有時間仍是好好磨練下吧~固然了此次也會放出相應的學習代碼,只須要在公衆號回覆【VRP去重】便可看到。

還有,寫到這裏,我忽然想起大一發生的一件糗事,那時候你們統一穿着綠色的軍裝。我去上完廁因此後忽然不知道本身屬於哪一個隊伍了。因而當時被教官狠狠訓了一頓。後來才知道,是三連。

推薦閱讀:
乾貨 | 想學習優化算法,不知從何學起?
乾貨 | 運籌學從何學起?如何快速入門運籌學算法?
乾貨 | 學習算法,你須要掌握這些編程基礎(包含JAVA和C++)
乾貨 | 算法學習必備訣竅:算法可視化解密
乾貨 | 模擬退火、禁忌搜索、迭代局部搜索求解TSP問題Python代碼分享

記得點個 在看 支持下哦~

本文分享自微信公衆號 - 程序猿聲(ProgramDream)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索