1. 程式人生 > >P問題、NP問題、NPC問題、NP-hard問題詳解

P問題、NP問題、NPC問題、NP-hard問題詳解

要理解P問題、NP問題、NPC問題、NP-hard問題,需要先弄懂幾個概念:

  • 什麼是多項式時間?
  • 什麼是確定性演算法?什麼是非確定性演算法?
  • 什麼是規約/約化?

多項式時間(Polynomial time)

什麼是時間複雜度?

時間複雜度並不是表示一個程式解決問題需要花多少時間,而是當程式所處理的問題規模擴大後,程式需要的時間長度對應增長得有多快。也就是說,對於某一個程式,其處理某一個特定資料的效率不能衡量該程式的好壞,而應該看當這個資料的規模變大到數百倍後,程式執行時間是否還是一樣,或者也跟著慢了數百倍,或者變慢了數萬倍。

不管資料有多大,程式處理所花的時間始終是那麼多的,我們就說這個程式很好,具有O

(1)O(1)的時間複雜度,也稱常數級複雜度;資料規模變得有多大,花的時間也跟著變得有多長,比如找n個數中的最大值這個程式的時間複雜度就是O(n)O(n),為線性級複雜度,而像氣泡排序、插入排序等,資料擴大2倍,時間變慢4倍的,時間複雜度是O(n2)O(n^2),為平方級複雜度。還有一些窮舉類的演算法,所需時間長度成幾何階數上漲,這就是O(an)O(a^n)指數級複雜度,甚至O(n!)O(n!)階乘級複雜度

不會存在 KaTeX parse error: Expected 'EOF', got '\*' at position 4: O(2\̲*̲n^2)

的複雜度,因為前面的那個"2"是係數,根本不會影響到整個程式的時間增長。同樣地,O(n3+n2)O(n^3+n^2) 的複雜度也就是O(n3)O(n^3)的複雜度。因此,我們會說,一個KaTeX parse error: Expected 'EOF', got '\*' at position 7: O(0.01\̲*̲n^3)的程式的效率比O(100*n^2)的效率低,儘管在n很小的時候,前者優於後者,但後者時間隨資料規模增長得慢,最終O(n3)O(n^3)的複雜度將遠遠超過O(n2)O(n^2)。我們也說,O(n100)O(n^{100})的複雜度小於O
(1.01n)O(1.01^n)
的複雜度。

容易看出,前面的幾類複雜度被分為兩種級別,其中後者的複雜度無論如何都遠遠大於前者。像O(1)O(1),O(ln(n))O(\ln(n)),O(na)O(n^a)等,我們把它叫做多項式級複雜度,因為它的規模n出現在底數的位置;另一種像是O(an)O(a^n)O(n!)O(n!)等,它是非多項式級的複雜度,其複雜度計算機往往不能承受。當我們在解決一個問題時,我們選擇的演算法通常都需要是多項式級的複雜度,非多項式級的複雜度需要的時間太多,往往會超時,除非是資料規模非常小。

確定性演算法與非確定性演算法

確定性演算法:

設A是求解問題B的一個解決演算法,在演算法的整個執行過程中,每一步都能得到一個確定的解,這樣的演算法就是確定性演算法。

非確定性演算法:

設A是求解問題B的一個解決演算法,它將問題分解成兩部分,分別為猜測階段和驗證階段,其中

  • 猜測階段:在這個階段,對問題的一個特定的輸入例項x產生一個任意字串y,在演算法的每一次執行時,y的值可能不同,因此,猜測以一種非確定的形式工作。
  • 驗證階段:在這個階段,用一個確定性演算法(有限時間內)驗證。①檢查在猜測階段產生的y是否是合適的形式,如果不是,則演算法停下來並得到no;② 如果y是合適的形式,則驗證它是否是問題的解,如果是,則演算法停下來並得到yes,否則演算法停下來並得到no。它是驗證所猜測的解的正確性。

規約/約化

問題A可以約化為問題B,稱為“問題A可規約為問題B”,可以理解為問題B的解一定就是問題A的解,因此解決A不會難於解決B。由此可知問題B的時間複雜度一定大於等於問題A。

《演算法導論》中有一個例子:現在有兩個問題:求解一個一元一次方程和求解一個一元二次方程。那麼我們說,前者可以規約為後者,意即知道如何解一個一元二次方程那麼一定能解出一元一次方程。我們可以寫出兩個程式分別對應兩個問題,那麼我們能找到一個“規則”,按照這個規則把解一元一次方程程式的輸入資料變一下,用在解一元二次方程的程式上,兩個程式總能得到一樣的結果。這個規則即是:兩個方程的對應項係數不變,一元二次方程的二次項係數為0。

從規約的定義中我們看到,一個問題規約為另一個問題,時間複雜度增加了,問題的應用範圍也增大了。通過對某些問題的不斷規約,我們能夠不斷尋找複雜度更高,但應用範圍更廣的演算法來代替複雜度雖然低,但只能用於很小的一類問題的演算法。存在這樣一個NP問題,所有的NP問題都可以約化成它。換句話說,只要解決了這個問題,那麼所有的NP問題都解決了。這種問題的存在難以置信,並且更加不可思議的是,這種問題不只一個,它有很多個,它是一類問題。這一類問題就是傳說中的NPC問題,也就是NP-完全問題。

P類問題、NP類問題、NPC問題、NP難問題

  1. P類問題:能在多項式時間內可解的問題。

  2. NP類問題:在多項式時間內“可驗證”的問題。也就是說,不能判定這個問題到底有沒有解,而是猜出一個解來在多項式時間內證明這個解是否正確。即該問題的猜測過程是不確定的,而對其某一個解的驗證則能夠在多項式時間內完成。P類問題屬於NP問題,但NP類問題不一定屬於P類問題。

  3. NPC問題:存在這樣一個NP問題,所有的NP問題都可以約化成它。換句話說,只要解決了這個問題,那麼所有的NP問題都解決了。其定義要滿足2個條件:

  • 它是一個NP問題;
  • 所有NP問題都能規約到它。
  1. NP難問題:NP-Hard問題是這樣一種問題,它滿足NPC問題定義的第二條但不一定要滿足第一條(就是說,NP-Hard問題要比 NPC問題的範圍廣,NP-Hard問題沒有限定屬於NP),即所有的NP問題都能約化到它,但是他不一定是一個NP問題。NP-Hard問題同樣難以找到多項式的演算法,但它不列入我們的研究範圍,因為它不一定是NP問題。即使NPC問題發現了多項式級的演算法,NP-Hard問題有可能仍然無法得到多項式級的演算法。事實上,由於NP-Hard放寬了限定條件,它將有可能比所有的NPC問題的時間複雜度更高從而更難以解決。 以上四個問題之間的關係如下圖所示: Loading...

P=NP?

  • 此處會再次從不同的角度來討論P與NP的定義。

“P=NP?” 通常被認為是電腦科學最重要的問題。在很早的時候,就有個數學家毫不客氣的指出,P=NP? 是個愚蠢的問題,並且為了嘲笑它,專門在4月1號寫了一篇“論文”,稱自己證明了 P=NP。

首先,我們要搞清楚什麼是“P=NP?” 為此,我們必須先了解一下什麼是“演算法複雜度”。為此,我們又必須先了解什麼是“演算法”。我們可以簡單的把“演算法”想象成一臺機器,就跟絞肉機似的。我們給它一些“輸入”,它就給我們一些“輸出”。比如,絞肉機的輸入是肉末,輸出是肉渣。牛的輸入是草,輸出是奶。“加法器”的輸入是兩個整數,輸出是這兩個整數的和。“演算法理論”所討論的問題,就是如何設計這些機器,讓它們更加有效的工作。就像是說如何培育出優質的奶牛,吃進相同數量的草,更快的產出更多的奶。

世界上的計算問題,都需要“演算法”經過一定時間的工作(也叫“計算”),才能得到結果。計算所需要的時間,往往跟“輸入”的大小有關係。你的牛吃越是多的草,它就需要越是長時間才能把它們都變成奶。這種草和奶的轉換速度,通常被叫做“演算法複雜度”。演算法複雜度通常被表示為一個函式f(n),其中n是輸入的大小。比如,如果我們的演算法複雜度為n^2,那麼當輸入10個東西的時候,它需要100個單元的時間才能完成計算。當輸入100 個東西的時候,它需要10000個單元的時間才能完成。當輸入1000個數據的時候,它需要1000000個單元的時間。所謂的“P時間”,多項式時間,就是說這個複雜度函式f(n)是一個多項式。 “P=NP?”中的“P”,就是指所有這些複雜度為多項式的演算法的“集合”,也就是“所有”的複雜度為多項式的演算法。為了簡要的描述以下的內容,我定義一些術語:

  • “f(n)時間演算法”=“能夠在f(n)時間之內,解決某個問題的演算法”

當f(n)是個多項式(比如n2n^2)的時候,這就是“多項式時間演算法”(P時間演算法)。當f(n)是個指數函式(比如2n2^n)的時候,這就是“指數時間演算法”(EXPTIME演算法)。很多人認為NP問題就是需要指數時間的問題,而NP跟EXPTIME,其實是風馬牛不相及的。很顯然,P不等於EXPTIME,但是P是否等於NP,卻沒有一個結論。

現在我來解釋一下什麼是NP。通常的計算機,都是確定性(deterministic)的。它們在同一個時刻,只有一種行為。如果用程式來表示,那麼它們遇到一個條件判斷(分支)的時候,只能一次探索其中一條路徑。比如:

if (x == 0) {
    one();
}
else {
    two();
}

在這裡,根據x的值是否為零,one()和two()這兩個操作,只有一個會發生。然而,有人幻想出來一種機器,叫做“非確定性計算機”(nondeterministic computer),它可以同時執行這程式的兩個分支,one()和two()。這有什麼用處呢?它的用處就在於,當你不知道x的大小的時候,根據one()和two()是否“執行成功”,你可以推斷出x是否為零。這種方式可以同時探索多種可能性。這不是普通的“平行計算”,因為每當遇到一個分支點,非確定性計算機就會產生新的計算單元,用以同時探索這些路徑。這機器就像有“分身術”一樣。當這種分支點存在於迴圈(或者遞迴)裡面的時候,它就會反覆的產生新的計算單元,新的計算單元又產生更多的計算單元,就跟細胞分裂一樣。一般的計算機都沒有 這種“超能力”,它們只有固定數目的計算單元。所以他只能先探索一條路徑,失敗之後,再回過頭來探索另外一條。所以,它們似乎要多花一些時間才能得到結果。到這裡,基本的概念都有了定義,於是我們可以圓滿的給出P和NP的定義。P和NP是這樣兩個“問題的集合”:

P = “確定性計算機”能夠在“多項式時間”解決的所有問題 NP = “非確定性計算機”能夠在“多項式時間”解決的所有問題 (注意它們的區別,僅在於“確定性”或者是“非確定性”。)

“P=NP?”問題的目標,就是想要知道P和NP這兩個集合是否相等。為了證明兩個集合(A和 B)相等,一般都要證明兩個方向:

  1. A 包含 B;
  2. B 包含 A。

上一個標題中我們已經說過NP包含了P。因為任何一個非確定性機器,都能被當成一個確定性的機器來用。你只要不使用它的“超能力”,在每個分支點只探索一條路徑就行。所以“P=NP?”問題的關鍵,就在於P是否也包含了NP。也就是說,如果只使用確定性計算機,能否在多項式時間之內,解決所有非確定性計算機能在多項式時間內解決的問題。

我們來細看一下什麼是多項式時間(Polynomial time)。我們都知道,n2n^2是多項式,n1000000n^{1000000} 也是多項式。多項式與多項式之間,卻有天壤之別。把解決問題所需要的時間,用“多項式”這麼籠統的概念來描述,其實是非常不準確的做法。在實際的大規模應用中,n2n^2的演算法都嫌慢。能找到“多項式時間”的演算法,根本不能說明任何問題。對此,理論家們喜歡說,就算再大的多項式(比如 n1000000n^{1000000}),也不能和再小的指數函式(比如 1.0001n1.0001^n)相比。因為總是“存在”一個M,當n>M的時候,1.0001n1.0001^n會超過n1000000n^{1000000}。可是問題的關鍵,卻不在於M的“存在”,而在於它的“大小”。如果你的輸入必須達到天文數字才能讓指數函式超過多項式的話,那麼還不如就用指數複雜度的演算法。所以,“P=NP?”這問題的錯誤就在於,它並沒有針對我們的實際需要,而是首先假設了我們有“無窮大”的輸入,有“無窮多”的時間和耐心,可以讓多項式時間的演算法“最終”得到優勢。

Reference:

本部落格與https://xuyunkun.com同步更新