時間復雜度 P/NP/NPC

你會經常看到網上出現“這怎么做,這不是NP問題嗎”、“這個只有搜了,這已經被證明是NP問題了”之類的話。你要知道,大多數人此時所說的NP問題其實都是指的NPC問題。他們沒有搞清楚NP問題和NPC問題的概念。NP問題并不是那種“只有搜才行”的問題,NPC問題才是。好,行了,基本上這個誤解已經被澄清了。下面的內容都是在講什么是P問題,什么是NP問題,什么是NPC問題,你如果不是很感興趣就可以不看了。接下來你可以看到,把NP問題當成是 NPC問題是一個多大的錯誤。

還是先用幾句話簡單說明一下時間復雜度。時間復雜度并不是表示一個程序解決問題需要花多少時間,而是當問題規模擴大后,程序需要的時間長度增長得有多快。也就是說,對于高速處理數據的計算機來說,處理某一個特定數據的效率不能衡量一個程序的好壞,而應該看當這個數據的規模變大到數百倍后,程序運行時間是否還是一樣,或者也跟著慢了數百倍,或者變慢了數萬倍。不管數據有多大,程序處理花的時間始終是那么多的,我們就說這個程序很好,具有O(1)的時間復雜度,也稱常數級復雜度;數據規模變得有多大,花的時間也跟著變得有多長,這個程序的時間復雜度就是O(n),比如找n個數中的最大值;而像冒泡排序、插入排序等,數據擴大2倍,時間變慢4倍的,屬于O(n^2)的復雜度。還有一些窮舉類的算法,所需時間長度成幾何階數上漲,這就是O(a^n)的指數級復雜度,甚至O(n!)的階乘級復雜度。不會存在O(2*n^2)的復雜度,因為前面的那個“2”是系數,根本不會影響到整個程序的時間增長。同樣地,O (n^3+n^2)的復雜度也就是O(n^3)的復雜度。因此,我們會說,一個O(0.01*n^3)的程序的效率比O(100*n^2)的效率低,盡管在n很小的時候,前者優于后者,但后者時間隨數據規模增長得慢,最終O(n^3)的復雜度將遠遠超過O(n^2)。我們也說,O(n^100)的復雜度小于O(1.01^n)的復雜度。

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

自然地,人們會想到一個問題:會不會所有的問題都可以找到復雜度為多項式級的算法呢?很遺憾,答案是否定的。有些問題甚至根本不可能找到一個正確的算法來,這稱之為“不可解問題”(Undecidable Decision Problem)。The Halting Problem就是一個著名的不可解問題,在我的Blog上有過專門的介紹和證明。再比如,輸出從1到n這n個數的全排列。不管你用什么方法,你的復雜度都是階乘級,因為你總得用階乘級的時間打印出結果來。有人說,這樣的“問題”不是一個“正規”的問題,正規的問題是讓程序解決一個問題,輸出一個“YES”或“NO”(這被稱為判定性問題),或者一個什么什么的最優值(這被稱為最優化問題)。那么,根據這個定義,我也能舉出一個不大可能會有多項式級算法的問題來:Hamilton回路。問題是這樣的:給你一個圖,問你能否找到一條經過每個頂點一次且恰好一次(不遺漏也不重復)最后又走回來的路(滿足這個條件的路徑叫做Hamilton回路)。這個問題現在還沒有找到多項式級的算法。事實上,這個問題就是我們后面要說的NPC問題。

下面引入P類問題的概念:如果一個問題可以找到一個能在多項式的時間里解決它的算法,那么這個問題就屬于P問題。P是英文單詞多項式的第一個字母。哪些問題是P類問題呢?通常NOI和NOIP不會出不屬于P類問題的題目。我們常見到的一些信息奧賽的題目都是P問題。道理很簡單,一個用窮舉換來的非多項式級時間的超時程序不會涵蓋任何有價值的算法。

接下來引入NP問題的概念。這個就有點難理解了,或者說容易理解錯誤。在這里強調(回到我竭力想澄清的誤區上),NP問題不是非P類問題。NP問題是指可以在多項式的時間里驗證一個解的問題。NP問題的另一個定義是,可以在多項式的時間里猜出一個解的問題。比方說,我RP很好,在程序中需要枚舉時,我可以一猜一個準。現在某人拿到了一個求最短路徑的問題,問從起點到終點是否有一條小于100個單位長度的路線。它根據數據畫好了圖,但怎么也算不出來,于是來問我:你看怎么選條路走得最少?我說,我RP很好,肯定能隨便給你指條很短的路出來。然后我就胡亂畫了幾條線,說就這條吧。那人按我指的這條把權值加起來一看,嘿,神了,路徑長度98,比100小。于是答案出來了,存在比100小的路徑。別人會問他這題怎么做出來的,他就可以說,因為我找到了一個比100 小的解。在這個題中,找一個解很困難,但驗證一個解很容易。驗證一個解只需要O(n)的時間復雜度,也就是說我可以花O(n)的時間把我猜的路徑的長度加出來。那么,只要我RP好,猜得準,我一定能在多項式的時間里解決這個問題。我猜到的方案總是最優的,不滿足題意的方案也不會來騙我去選它。這就是NP問題。當然有不是NP問題的問題,即你猜到了解但是沒用,因為你不能在多項式的時間里去驗證它。下面我要舉的例子是一個經典的例子,它指出了一個目前還沒有辦法在多項式的時間里驗證一個解的問題。很顯然,前面所說的Hamilton回路是NP問題,因為驗證一條路是否恰好經過了每一個頂點非常容易。但我要把問題換成這樣:試問一個圖中是否不存在Hamilton回路。這樣問題就沒法在多項式的時間里進行驗證了,因為除非你試過所有的路,否則你不敢斷定它“沒有Hamilton回路”。

之所以要定義NP問題,是因為通常只有NP問題才可能找到多項式的算法。我們不會指望一個連多項式地驗證一個解都不行的問題存在一個解決它的多項式級的算法。相信讀者很快明白,信息學中的號稱最困難的問題——“NP問題”,實際上是在探討NP問題與P類問題的關系。

很顯然,所有的P類問題都是NP問題。也就是說,能多項式地解決一個問題,必然能多項式地驗證一個問題的解——既然正解都出來了,驗證任意給定的解也只需要比較一下就可以了。關鍵是,人們想知道,是否所有的NP問題都是P類問題。我們可以再用集合的觀點來說明。如果把所有P類問題歸為一個集合P中,把所有 NP問題劃進另一個集合NP中,那么,顯然有P屬于NP。現在,所有對NP問題的研究都集中在一個問題上,即究竟是否有P=NP?通常所謂的“NP問題”,其實就一句話:證明或推翻P=NP。

NP問題一直都是信息學的巔峰。巔峰,意即很引人注目但難以解決。在信息學研究中,這是一個耗費了很多時間和精力也沒有解決的終極問題,好比物理學中的大統一和數學中的歌德巴赫猜想等。目前為止這個問題還“啃不動”。但是,一個總的趨勢、一個大方向是有的。人們普遍認為,P=NP不成立,也就是說,多數人相信,存在至少一個不可能有多項式級復雜度的算法的NP問題。人們如此堅信P≠NP是有原因的,就是在研究NP問題的過程中找出了一類非常特殊的NP問題叫做NP-完全問題,也即所謂的 NPC問題。C是英文單詞“完全”的第一個字母。正是NPC問題的存在,使人們相信P≠NP。下文將花大量篇幅介紹NPC問題,你從中可以體會到NPC問題使P=NP變得多么不可思議。

為了說明NPC問題,我們先引入一個概念——約化(Reducibility,有的資料上叫“歸約”)。

簡單地說,一個問題A可以約化為問題B的含義即是,可以用問題B的解法解決問題A,或者說,問題A可以“變成”問題B。《算法導論》上舉了這么一個例子。比如說,現在有兩個問題:求解一個一元一次方程和求解一個一元二次方程。那么我們說,前者可以約化為后者,意即知道如何解一個一元二次方程那么一定能解出一元一次方程。我們可以寫出兩個程序分別對應兩個問題,那么我們能找到一個“規則”,按照這個規則把解一元一次方程程序的輸入數據變一下,用在解一元二次方程的程序上,兩個程序總能得到一樣的結果。這個規則即是:兩個方程的對應項系數不變,一元二次方程的二次項系數為0。按照這個規則把前一個問題轉換成后一個問題,兩個問題就等價了。同樣地,我們可以說,Hamilton回路可以約化為TSP問題(Travelling Salesman Problem,旅行商問題):在Hamilton回路問題中,兩點相連即這兩點距離為0,兩點不直接相連則令其距離為1,于是問題轉化為在TSP問題中,是否存在一條長為0的路徑。Hamilton回路存在當且僅當TSP問題中存在長為0的回路。

“問題A可約化為問題B”有一個重要的直觀意義:B的時間復雜度高于或者等于A的時間復雜度。也就是說,問題A不比問題B難。這很容易理解。既然問題A能用問題B來解決,倘若B的時間復雜度比A的時間復雜度還低了,那A的算法就可以改進為B的算法,兩者的時間復雜度還是相同。正如解一元二次方程比解一元一次方程難,因為解決前者的方法可以用來解決后者。

很顯然,約化具有一項重要的性質:約化具有傳遞性。如果問題A可約化為問題B,問題B可約化為問題C,則問題A一定可約化為問題C。這個道理非常簡單,就不必闡述了。

現在再來說一下約化的標準概念就不難理解了:如果能找到這樣一個變化法則,對任意一個程序A的輸入,都能按這個法則變換成程序B的輸入,使兩程序的輸出相同,那么我們說,問題A可約化為問題B。

當然,我們所說的“可約化”是指的可“多項式地”約化(Polynomial-time Reducible),即變換輸入的方法是能在多項式的時間里完成的。約化的過程只有用多項式的時間完成才有意義。

好了,從約化的定義中我們看到,一個問題約化為另一個問題,時間復雜度增加了,問題的應用范圍也增大了。通過對某些問題的不斷約化,我們能夠不斷尋找復雜度更高,但應用范圍更廣的算法來代替復雜度雖然低,但只能用于很小的一類問題的算法。再回想前面講的P和NP問題,聯想起約化的傳遞性,自然地,我們會想問,如果不斷地約化上去,不斷找到能“通吃”若干小NP問題的一個稍復雜的大NP問題,那么最后是否有可能找到一個時間復雜度最高,并且能“通吃”所有的 NP問題的這樣一個超級NP問題?答案居然是肯定的。也就是說,存在這樣一個NP問題,所有的NP問題都可以約化成它。換句話說,只要解決了這個問題,那么所有的NP問題都解決了。這種問題的存在難以置信,并且更加不可思議的是,這種問題不只一個,它有很多個,它是一類問題。這一類問題就是傳說中的NPC 問題,也就是NP-完全問題。NPC問題的出現使整個NP問題的研究得到了飛躍式的發展。我們有理由相信,NPC問題是最復雜的問題。再次回到全文開頭,我們可以看到,人們想表達一個問題不存在多項式的高效算法時應該說它“屬于NPC問題”。此時,我的目的終于達到了,我已經把NP問題和NPC問題區別開了。到此為止,本文已經寫了近5000字了,我佩服你還能看到這里來,同時也佩服一下自己能寫到這里來。

NPC問題的定義非常簡單。同時滿足下面兩個條件的問題就是NPC問題。首先,它得是一個NP問題;然后,所有的NP問題都可以約化到它。證明一個問題是 NPC問題也很簡單。先證明它至少是一個NP問題,再證明其中一個已知的NPC問題能約化到它(由約化的傳遞性,則NPC問題定義的第二條也得以滿足;至于第一個NPC問題是怎么來的,下文將介紹),這樣就可以說它是NPC問題了。

既然所有的NP問題都能約化成NPC問題,那么只要任意一個NPC問題找到了一個多項式的算法,那么所有的NP問題都能用這個算法解決了,NP也就等于P 了。因此,給NPC找一個多項式算法太不可思議了。因此,前文才說,“正是NPC問題的存在,使人們相信P≠NP”。我們可以就此直觀地理解,NPC問題目前沒有多項式的有效算法,只能用指數級甚至階乘級復雜度的搜索。

順便講一下NP-Hard問題。NP-Hard問題是這樣一種問題,它滿足NPC問題定義的第二條但不一定要滿足第一條(就是說,NP-Hard問題要比 NPC問題的范圍廣)。NP-Hard問題同樣難以找到多項式的算法,但它不列入我們的研究范圍,因為它不一定是NP問題。即使NPC問題發現了多項式級的算法,NP-Hard問題有可能仍然無法得到多項式級的算法。事實上,由于NP-Hard放寬了限定條件,它將有可能比所有的NPC問題的時間復雜度更高從而更難以解決。

不要以為NPC問題是一紙空談。NPC問題是存在的。

邏輯電路問題屬于NPC問題。這是有嚴格證明的。它顯然屬于NP問題,并且可以直接證明所有的NP問題都可以約化到它(不要以為NP問題有無窮多個將給證明造成不可逾越的困難)。證明過程相當復雜,其大概意思是說任意一個NP問題的輸入和輸出都可以轉換成邏輯電路的輸入和輸出(想想計算機內部也不過是一些 0和1的運算),因此對于一個NP問題來說,問題轉化為了求出滿足結果為True的一個輸入(即一個可行解)。

有了第一個NPC問題后,一大堆NPC問題就出現了,因為再證明一個新的NPC問題只需要將一個已知的NPC問題約化到它就行了。后來,Hamilton 回路成了NPC問題,TSP問題也成了NPC問題。現在被證明是NPC問題的有很多,任何一個找到了多項式算法的話所有的NP問題都可以完美解決了。因此說,正是因為NPC問題的存在,P=NP變得難以置信。P=NP問題還有許多有趣的東西,有待大家自己進一步的挖掘。攀登這個信息學的巔峰是我們這一代的終極目標。現在我們需要做的,至少是不要把概念弄混淆了。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/445623.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/445623.shtml
英文地址,請注明出處:http://en.pswp.cn/news/445623.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

kmp1-HDU1711 HDU1686 HDU2087 HDU3746

HDU 1711 kmp模板題 http://acm.hdu.edu.cn/showproblem.php?pid1711 #include<stdio.h> #include<string.h> #define N 1000005 int s[N]; int p[N]; int next[N]; int m,n; void getnext(){int j0,k-1;next[0]-1;while(j<m){if(k-1||p[j]p[k]){j;k;next[j]…

kmp2-HDU1358 HUST1010 POJ2406 POJ2752

HDU1358 http://acm.hdu.edu.cn/showproblem.php?pid1358 先構造出 next[] 數組&#xff0c;下標為 i&#xff0c;定義一個變量 j i - next[i] 就是next數組下標和下標對應值的差&#xff0c;如果這個差能整除下標 i&#xff0c;即 i%j0 ,則說明下標i之前的字符串&#xff0…

18暑期培訓總結

暑假一共直播講了七次課&#xff0c;每次一小時到一個半小時&#xff0c;前六次講解python主要實用語法&#xff0c;最后一次講了學習方法和簡單基礎的思想和算法。由于時間有限&#xff0c;不能做到很好&#xff0c;請見諒。 學院做題網站&#xff1a;橙白oj http://oj.acm-i…

第七次課 課上代碼

時間空間復雜度&#xff08;例子&#xff1a;1-n求和&#xff09; 復雜度&#xff1a;https://blog.csdn.net/hebtu666/article/details/82463970 https://blog.csdn.net/hebtu666/article/details/82465495 二分 一個數組查找某個值1 2 3 5 6 7 8 9 10 15 20。。 查找11 …

數據結構課上筆記1

第一節課復習了c語言的一些知識&#xff0c;并簡單介紹了數據結構這門課程。 1、引用和函數調用&#xff1a; 1.1引用&#xff1a;對一個數據建立一個“引用”&#xff0c;他的作用是為一個變量起一個別名。這是C對C語言的一個重要補充。 用法很簡單&#xff1a; int a 5; …

并查集實現

并查集是什么東西&#xff1f; 它是用來管理元素分組情況的一種數據結構。 他可以高效進行兩個操作&#xff1a; 查詢a&#xff0c;b是否在同一組合并a和b所在的組 萌新可能不知所云&#xff0c;這個結構到底有什么用&#xff1f; 經分析&#xff0c;并查集效率之高超乎想象…

字符串上的簡單動態規劃

因為數據結構快學串了&#xff0c;以前又做過一些字符串dp的題&#xff0c;今天突然就想把它們寫在一起吧。 直接開始 問題1&#xff1a;給兩個字符串&#xff0c;求最長公共子串 問題2&#xff1a;給兩個字符串&#xff0c;求最長公共子序列 問題3&#xff1a;給一個字符串…

線段樹簡單實現

首先&#xff0c;線段樹是一棵滿二叉樹。&#xff08;每個節點要么有兩個孩子&#xff0c;要么是深度相同的葉子節點&#xff09; 每個節點維護某個區間&#xff0c;根維護所有的。 如圖&#xff0c;區間是二分父的區間。 當有n個元素&#xff0c;初始化需要o(n)時間&#xf…

樹狀數組實現

樹狀數組能夠完成如下操作&#xff1a; 給一個序列a0-an 計算前i項和 對某個值加x 時間o(logn) 注意&#xff1a;有人覺得前綴和就行了&#xff0c;但是你還要維護啊&#xff0c;改變某個值&#xff0c;一個一個改變前綴和就是o(n)了。 線段樹樹狀數組的題就是這樣&#x…

數據結構課上筆記2

今天繼續說明了一些基本概念&#xff0c;講解了時間空間復雜度。 &#xff08;對于概念的掌握也很重要&#xff09; 元素之間的關系在計算機中有兩種表示方法&#xff1a;順序映像和非順序映像&#xff0c;由此得到兩種不同的儲存結構&#xff1a; 順序存儲結構和鏈式存儲結構…

雙端單調隊列

上次我們介紹了單調棧結構https://blog.csdn.net/hebtu666/article/details/82717317 這次介紹一種新的數據結構&#xff1a;雙端隊列&#xff1a;雙端隊列是指允許兩端都可以進行入隊和出隊操作的隊列&#xff0c;其元素的邏輯結構仍是線性結構。將隊列的兩端分別稱為前端和后…

KMP子字符串匹配算法學習筆記

文章目錄學習資源什么是KMP什么是前綴表為什么一定要用前綴表如何計算前綴表前綴表有什么問題使用next數組來匹配放碼過來構造next數組一、初始化二、處理前后綴不相同的情況三、處理前后綴相同的情況使用next數組來做匹配代碼總覽測試代碼時間復雜度分析學習資源 字符串&…

數組實現隊列

數組實現隊列結構&#xff1a; 相對棧結構要難搞一些&#xff0c;隊列的先進先出的&#xff0c;需要一個數組和三個變量&#xff0c;size記錄已經進來了多少個元素&#xff0c;不需要其它萌新看不懂的知識。 觸底反彈&#xff0c;頭尾追逐的感覺。 循環使用數組。 具體解釋…

棧/隊列 互相模擬實現

用兩個棧來實現一個隊列&#xff0c;完成隊列的Push和Pop操作。 隊列中的元素為int類型。 思路&#xff1a;大概這么想&#xff1a;用一個輔助棧把進第一個棧的元素倒一下就好了。 比如進棧1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5 第一個棧&#xff1a; 5 …

數據結構課上筆記3

這節課介紹了線性表結構和順序表示的一部分內容。 操作太多&#xff0c;而且書上有&#xff0c;就不一一介紹分析了。 線性表定義&#xff1a;n個數據元素的有限序列。 特點&#xff1a; 存在唯一一個稱作“第一個”的元素。存在唯一一個稱作“最后一個”的元素除最后一個元…

內存分區

之前一直比較懵&#xff0c;想想還是單獨寫一個短篇來記錄吧 一般內存主要分為&#xff1a;代碼區、常量區、靜態區&#xff08;全局區&#xff09;、堆區、棧區這幾個區域。 代碼區&#xff1a;存放程序的代碼&#xff0c;即CPU執行的機器指令&#xff0c;并且是只讀的。 常…

棧的排序

一個棧中元素的類型為整型&#xff0c;現在想將該棧從頂到底按從大到小的順序排序&#xff0c;只許申請一個棧。除此之外&#xff0c;可以申請新的變量&#xff0c;但是不能申請額外的數據結構&#xff0c;如何完成排序&#xff1f; 思路&#xff1a; 將要排序的棧記為stack,申…

雙鏈表實現

以前寫的不帶頭的單鏈表實現&#xff0c;當時也啥也沒學&#xff0c;好多東西不知道&#xff0c;加上一心想壓縮代碼&#xff0c;減少情況&#xff0c;所以寫得不太好。 請教了老師&#xff0c;首先是命名問題和代碼緊湊性等的改進。還有可讀性方面的改進&#xff0c;多寫了一…

數據結構作業1 講解和拓展

原題來自雪梨教育 http://www.edu2act.net/task/list/checked/ 題后給出講解和擴展 任務1_1 比較下列算法的時間復雜度 任務描述&#xff1a; 下面給出4個算法&#xff0c;請分析下列各算法的時間復雜度&#xff0c;請寫清楚題號&#xff0c;并將每個小題的分析過程寫出來&…

KMP+DP1

Description 求一個字符串的所有前綴在串中出現的次數之和 Input 多組用例&#xff0c;每組用例占一行為一個長度不超過100000的字符串&#xff0c;以文件尾結束輸入 Output 對于每組用例&#xff0c;輸出該字符串的所有前綴在串中出現的次數之和&#xff0c;結果模256 Samp…