學習 程序員小灰——《什么是旅行商問題》筆記:
旅行商問題
旅行商問題所描述的是這樣一個場景:
有一個商品推銷員,要去若干個城市推銷商品。該推銷員從一個城市出發,需要經過所有城市后,回到出發地。每個城市之間都有道路連通,且距離各不相同,推銷員應該如何選擇路線,使得總行程最短呢?
這個問題看起來很簡單,卻很難找到一個真正高效的求解算法。其中最容易想到的,是使用窮舉法:
把所有可能的路線窮舉出來,計算出每一條路線的總行程:
A-B-C-D-E-F-G-H-I
A-B-C-D-E-F-G-I-H
A-B-C-D-E-F-H-G-I
A-B-C-D-E-F-H-I-G
A-B-C-D-E-F-I-H-G
A-B-C-D-E-F-I-G-H
......
......
像上面這樣排列組合,從所有路線中找出總行程最短的路線。顯然,這個方法的時間復雜度是O(n!),隨着城市數量的增長,花費的運算時間簡直不可想象!
后來,人們想出了許多相對優化的解決方案,比如動態規划法、分枝定界法(這個算法很有意思,以后會專門寫一篇漫畫來詳細介紹)......但是,這些算法的時間復雜度仍然是指數級的,並沒有讓性能問題得到根本的解決。
P和NP
NP到底是什么意思呢?
我們曾經學習過許許多多的算法,這些算法的時間復雜度都可以用多項式來表示,比如:
歸並排序的時間復雜度是O(nlogn)
冒泡排序的時間復雜度是O(n^2)
Floyd算法的時間復雜度是O(n^3)
盡管這些算法的運行時間有數量級上的差別,但是它們的時間復雜度都可以用O(n^k)來表示,k是一個常數。
因此,這些算法都是多項式時間算法,能用多項式時間算法解決的問題被稱為P問題( Polynomial)。
人們常說,能用錢解決的問題都不是問題,在計算機科學家眼中,能用多項式時間解決的問題都不是問題。
然而,世間還存在許多變態的問題,是無法(至少是暫時無法)在多項式時間內解決的,比如一些算法的時間復雜度是O(2^n),甚至O(n!)。
隨着問題規模n的增長,計算量的增長速度是非常恐怖的。這類問題被稱為NP問題(Non-deterministic Polynomial),意思是“不確定是否能用多項式時間解決”。
有些科學家認為,所有的NP問題終究都可以在多項式時間內解決,只是我們暫時還沒有找到方法;也有些科學家認為,某些NP問題永遠無法在多項式時間內解決。
這個業界爭論可以用一個公式來表達:
NP = P?
歸約和NPC
這里所說的NPC問題可不是游戲當中的NPC,它究竟是什么意思呢?要想理解NPC問題,我們需要先了解歸約的概念。
歸約,可以簡單理解成問題之間的轉化。例如問題Q是一個一元一次方程的求解問題:3x+6 = 12,這個問題可以轉化成一個一元二次問題Q':0x^2+3x+6 = 12。
顯然,問題Q並不比問題Q'更難解決,只要有辦法解決Q',就一定能夠解決Q。對於這種情況,我們可以說問題Q歸約於問題Q'。
同時,這種歸約可以逐級傳遞,比如問題A歸約於問題B,問題B歸約於問題C,問題C歸約於問題D,那么我們可以說問題A歸約於問題D。
在NP問題之間,也可以存在歸約關系。我們把眾多的NP問題層層歸約,必定會得到一個或多個“終極問題”,這些歸約的終點就是所謂的NPC問題(NP-complete),也可以翻譯成PC完全問題。上面所講的旅行商問題,被科學家證明屬於NPC問題。

就數量上而言,NP問題遠比P問題要多,而NP之中的NPC問題也僅占極少數,所以P、NP、NPC之間的關系可以用下圖來表示:

俗話說擒賊先擒王,只要有朝一日,我們能夠找到NPC問題的多項式時間算法,就能夠解決掉所有的NP問題!但遺憾的是,至今還沒有人能夠找到可行的方法,很多人認為這些問題是無解的。
回到最初的問題:
既然是工程問題,我們與其鑽牛角尖尋求最優解,不如用小得多的代價尋求次優解。
最簡單的辦法是使用貪心算法,先選擇距離起點最近的城市A,再選擇距離A城市最近的城市B... 以此類推,每一步都保證局部最優。
這樣規划出的路線雖然未必是全局最優,但平均情況下也不會比最優方案差多少。
除此之外,還有許多近似的解決方案,比如遺傳算法、蟻群算法等等。谷歌有一款開源工具OR-TOOL,當中也包含路線規划的實現,可以研究一下。
總結:
本文提到的算法名及可擴展內容:
窮舉法、動態規划法、分枝定界法
歸並排序、冒泡排序、Floyd算法
貪心算法
遺傳算法、蟻群算法
谷歌開源工具OR-TOOL
