tsp的理論和實踐系列(1)最簡單的實踐

tsp目錄java

  1. 最簡單的實踐 juejin.im/post/5d1b14…
  2. 最簡單的理論 juejin.im/post/5d1b15…
  3. 當前tsp的現狀 juejin.im/post/5d1b19…
  4. 單起點任務分配 juejin.im/post/5d1c6d…
  5. 多起點任務分配 juejin.im/post/5d1dbe…
  6. 更簡潔的多起點分配 juejin.im/post/5d1dc2…
  7. 起點時間窗 juejin.im/post/5d1f1f…
  8. 終點時間窗和hk juejin.im/post/5d1f44…
  9. LK簡介 juejin.im/post/5d25b8…
  10. tsp系列暫停一下 juejin.im/post/5d302e…

貨郎擔又名旅行商, 英文縮寫tsp, 它描述的是這樣一個問題: 一個商人從起點出發, 順序通過全部點以後回到起點的最短路徑(哈密爾頓迴路)算法

問題的背景和限制條件
  • 做爲一家物流公司的CTO, 我們遇到的第一個問題是: 配送員如何可以在8小時內配送更多的貨物, 所以, 尋找一條配送的最短路徑就是現實的問題.
  • 實際空間的點都用經緯度表示, 由於地圖廠商對於大量的查詢會收取響應的費用, 更爲關鍵的是每次對於地圖廠商的接口調用都會造成至關的時間開銷, 這個是配送系統沒法忍受的. 所以只能採用直線距離或者直角距離(又名閔可夫斯基一階距離, 閔可夫斯基是一個偉大的人, 在理論篇會有着重的描寫)
  • 一個配送員一個時間段(或者說一車)配送的單量大約在20-50之間, 數量級不會大於100.
曲折的解決方案和結論

當時, 咱們採用的是蟻羣算法, 最終咱們拋棄了它, 由於:數組

  1. 他的收斂的隨機性太大.
  2. 他的算法精度再某些狀況下會特別的有問題, 尤爲是點之間的距離差距很大的狀況下, 大尺度的點距徹底會掩蓋掉那些比較近的點之間的線路的優化, 也就是說, 比較近的點之間的線路是混亂的.

固然, 通過簡單的一點點修正, 他的精度會大幅提高, 可是, 咱們不如用更節省時間和空間的算法來搞定. 好比最遠插入法. 本文末尾會再介紹優化算法, 這個優化對於全部的不許確算法都是有用的(實際上是一個簡單且不充分的LK算法 - 沒錯就是HLK的基礎算法, 用在這種條件下剛恰好)bash

最遠插入法

因此, 這種狀況下, 個人建議是使用最遠插入法, 最遠插入的規則是:數據結構

  1. 找到起點和距離起點最遠的點, 若是沒有起點, 就找點集中最遠的兩個點.
  2. 再找點集中距離這兩個點最遠的點. 而後判斷這個新點插入哪一個位置更好.
  3. 重複找距離已選擇點集最遠的點, 再判斷位置.
  4. 最後一次點集包含了n-1個點, 外面只有一個點, 所以他要判斷n-1次找到最合理的位置.

這個算法的開銷: 判斷位置=1+2+3+…..(n-1), 總開銷=n*n/2, 一個至關快速的多項式時間算法, 惋惜他的結果並非精確解. 具體理由能夠參考理論篇.post

算法的核心是數據結構, 不給數據結構的算法都是耍流氓, 好懷念上學時看的俄國大叔的算法書, 都是數據結構引領算法的. 這個算法也是同樣的, 要達到理論開銷, 我們要解決的核心問題是: 排序以及用什麼數據結構去排序. 這裏考慮最直觀的數據結構矩陣(也就是二維數組)優化

二維數組d[x][y]存儲從x到y的距離, x, y是一維數組配送點的集合.
那麼d[0]就是0到全部點的距離的集合(一個數組): d[0][1],d[0][2].....d[0][n]
注意, 深度分析以後, 咱們發現, 其實我們不須要排序, 我們只需找到最遠距離就行了.

1. 在第一步, 咱們只需給起點數組排序, 好比起點是0號點, d[0]裏面的元素找到最遠距離. 這裏面好比d[0][5]是最遠的.
2. 第二步, 此時咱們須要一個數據結構, 保存全部點到咱們路徑裏面的點的距離, 這個用一個鍵值對就能夠了, 再js裏面這是一個對象, 好比 var ld={}, 這裏面的每一個鍵都是不在路徑中的點. 咱們目前須要合併d[0]和d[5]裏面的距離, 直接保留小(小的纔是距離)的那個在ld這個鍵值對裏面. 
3. 而後, 再ld裏面找到最大的值, 這個值的鍵就是距離 ( 0, 5), 最遠的的那個點, 好比是 3, 而後再把 3 插入到( 0, 5)這個路徑裏面, 路徑建議使用一個鏈表保存(由於有順序而且要常常作插入操做).
4. 每一步都是這樣的, 每一步都引入一個數組, 而後合併到鍵值對裏面, 拿到距離, 而後, 把距離最遠的點跳出來, 插入路徑鏈表裏面. 
複製代碼

因此這個算法須要的數據結構:spa

  1. 距離矩陣, 能夠直接使用二維數組.
  2. 路徑距離的鍵值對, 可使用js對象(map也能夠).
  3. 生成的路徑使用鏈表, 這個造一個帶指針(引用)的對象就能夠了, 或者引入一個相似Lodash這樣的庫.

算法的最終結構要平衡幾個方面:3d

  1. 數據結構清晰合理(不見得利於理解, 可是必定要保持最簡模型).
  2. 算法快速準確, 保持足夠的快速而且足夠的精確.
  3. 總體邏輯和思路簡潔, 內聚高, 外延少, 最好能成爲一個封閉系統, 外部調用不須要理解他的內部結構.
小數量級狀況下最簡單的LK交換優化方案
  • 蟻羣算法搭配這個以後, 最終路徑主要是這個算法決定的, 蟻羣其實只是給出一個基礎的能跑通的就行, 所以蟻羣的循環次數甚至能夠設爲一, 也就是隻要能跑出一個通路就OK, 那麼既然如此, 爲啥要用蟻羣這麼複雜的方案呢? 直接最遠插入就行了.
  • 簡單的說就是作兩次循環, 而後交換點嘗試, 貼個代碼進來吧:
while(這裏設置一個合理的輪次數或者判斷標準, 由於每次交換以後都須要再重頭嘗試交換) {
            for (i = 1; i < pointCount - 1; ++i) {
                for (j = i + 1; j < pointCount; ++j) {
                  //這裏把i,j兩個點交換, 而後計算新的路徑總長度.
                    if (newLength < length) {
                      //這個把此次成功的交換記錄到最終結果.而後開始下一輪嘗試
                        break;
                    }
                }
            }
        }
複製代碼
相關文章
相關標籤/搜索