NP 完全性理論與近似算法

算法愛好者2018-06-14 21:39:27

(點擊上方公眾號,可快速關注)


來源:獨酌逸醉

www.cnblogs.com/chinazhangjie/archive/2010/12/06/1898070.html

一、圖靈機

根據有限狀態控制器的當前狀態及每個讀寫頭讀到的帶符號,圖靈機的一個計算步可實現下面3個操作之一或全部。

  1. 改變有限狀態控制器中的狀態。

  2. 清除當前讀寫頭下的方格中原有帶符號並寫上新的帶符號。

  3. 獨立地將任何一個或所有讀寫頭,向左移動一個方格(L)或向右移動一個方格(R)或停在當前單元不動(S)。


k帶圖靈機可形式化地描述為一個7元組(Q,T,I,δ,b,q0,qf),其中: 

  1. Q是有限個狀態的集合。

  2. T是有限個帶符號的集合。

  3. I是輸入符號的集合

  4. b是唯一的空白符,b∈T-I。

  5. q0是初始狀態。      

  6. qf是終止(或接受)狀態。

  7. δ是移動函數。


它是從Q×Tk的某一子集映射到Q×(T×{L,R,S})k的函數。

圖靈機M的時間複雜性T(n)是它處理所有長度為n的輸入所需的最大計算步數。如果對某個長度為n的輸入,圖靈機不停機,T(n)對這個n值無定義。

圖靈機的空間複雜性S(n)是它處理所有長度為n的輸入時,在k條帶上所使用過的方格數的總和。如果某個讀寫頭無限地向右移動而不停機,S(n)也無定義。

確定型圖靈機


有限狀態集Q,狀態q0:初始狀態;qy:接受狀態;狀態qn:不接受狀態。

字符集合{0, 1, b} ;其中b是空格符。

轉換功能:


輸入x = 101000b

執行順序:

q0輸入1 (q0,r)右移磁頭到0

q0輸入0 (q0,r)右移磁頭到1

q0輸入0 (q0,r)右移磁頭到0

...

q0輸入b (q1,l)左移磁頭到0

q1輸入0 (q2,b)

q2輸入b (q2,l)左移磁頭到0

q2輸入0 (q3,b)

q3輸入b (qy,l)退出

二、P類與NP類問題

一般地説,將可由多項式時間算法求解的問題看作是易處理的問題,而將需要超多項式時間才能求解的問題看作是難處理的問題。


有許多問題,從表面上看似乎並不比排序或圖的搜索等問題更困難,然而至今人們還沒有找到解決這些問題的多項式時間算法,也沒有人能夠證明這些問題需要超多項式時間下界。


在圖靈機計算模型下,這類問題的計算複雜性至今未知。

為了研究這類問題的計算複雜性,人們提出了另一個能力更強的計算模型,即計算模型,簡記為NDTM(Nondeterministic Turing Machine)。


在非確定性圖靈機計算模型下,許多問題可以在多項式時間內求解。


非確定性圖靈機


在圖靈機計算模型中,移動函數δ是單值的,即對於Q´Tk中的每一個值,當它屬於δ的定義域時,Q´(T´{L,RS})k中只有唯一的值與之對應,稱這種圖靈機為確定性圖靈機,簡記為DTM(Deterministic Turing Machine)

非確定性圖靈機(NDTM):一個k帶的非確定性圖靈機M是一個7元組:(Q,T,I,δ,b,q0,qf)。與確定性圖靈機不同的是非確定性圖靈機允許移動函數δ具有不確定性,即對於Q×Tk中的每一個值(q;x1,x2,…,xk),當它屬於δ的定義域時,Q×(T×{L,R,S})k中有唯一的一個子集δ(q;x1,x2,…,xk)與之對應。可以在δ(q;x1,x2,…,xk)中隨意選定一個值作為它的函數值。


P類與NP類語言定義


P={L|L是一個能在多項式時間內被一台DTM所接受的語言}

NP={L|L是一個能在多項式時間內被一台NDTM所接受的語言}

由於一台確定性圖靈機可看作是非確定性圖靈機的特例,所以可在多項式時間內被確定性圖靈機接受的語言也可在多項式時間內被非確定性圖靈機接受。故P Í NP。 


NP類語言舉例——無向圖的團問題


該問題的輸入是一個有n個頂點的無向圖G=(V,E)和一個整數k。要求判定圖G是否包含一個k頂點的完全子圖(團),即判定是否存在V’V,|V’|=k,且對於所有的u,v∈V’,有(u,v)∈E。


若用鄰接矩陣表示圖G,用二進制串表示整數k,則團問題的一個實例可以用長度為的二進位串表示。因此,團問題可表示為語言:

CLIQUE={w#v|w,v∈{0,1}*,以w為鄰接矩陣的圖G有一個k頂點的團,其中v是k的二進制表示。}


接受該語言CLIQUE的非確定性算法:用非確定性選擇指令選出包含k個頂點的候選頂點子集V,然後確定性地檢查該子集是否是團問題的一個解。算法分為3個階段


算法的第一階段將輸入串w#v分解,並計算出n = ,以及用v表示的整數k。若輸入不具有形式w#v或|w|不是一個平方數就拒絕該輸入。顯而易見,第一階段可在時間內完成。


在算法的第二階段中,非確定性地選擇V的一個k元子集V’V。

算法的第三階段是確定性地檢查V’的團性質。若V’是一個團則接受輸入,否則拒絕輸入。這顯然可以在時間內完成。因此,整個算法的時間複雜性為 。

非確定性算法在多項式時間內接受語言CLIQUE,故CLIQUE∈NP.


NP完全問題


PNP。


直觀上看,P類問題是確定性計算模型下的易解問題類,而NP類問題是非確定性計算模型下的易驗證問題類。

大多數的計算機科學家認為NP類中包含了不屬於P類的語言,即P≠NP。

NP完全問題有一種令人驚奇的性質,即如果一個NP完全問題能在多項式時間內得到解決,那麼NP中的每一個問題都可以在多項式時間內求解,即P = NP。

目前還沒有一個NP完全問題有多項式時間算法。

三、NP完全問題的近似算法

迄今為止,所有的NP完全問題都還沒有多項式時間算法。


對於這類問題,通常可採取以下幾種解題策略。

  1. 只對問題的特殊實例求解

  2. 用動態規劃法或分支限界法求解

  3. 用概率算法求解

  4. 只求近似解

  5. 用啟發式方法求解

近似算法的性能

若一個最優化問題的最優值為c*,求解該問題的一個近似算法求得的近似最優解相應的目標函數值為c,則將該近似算法的性能比定義為


在通常情況下,該性能比是問題輸入規模n的一個函數ρ(n),即

該近似算法的相對誤差定義為:。若對問題的輸入規模n,有一函數ε(n)使得則稱ε(n)為該近似算法的相對誤差界。近似算法的性能比ρ(n)與相對誤差界ε(n)之間顯然有如下關係:


近似算法


 問題描述:給定一個完全無向圖G=(V,E),其每一邊(u,v)∈E有一非負整數費用c(u,v)。要找出G的最小費用哈密頓迴路。

旅行售貨員問題的一些特殊性質:

 

比如,費用函數c往往具有三角不等式性質,即對任意的3個頂點u,v,w∈V,有:c(u,w)≤c(u,v)+c(v,w)。當圖G中的頂點就是平面上的點,任意2頂點間的費用就是這2點間的歐氏距離時,費用函數c就具有三角不等式性質。


1  滿足三角不等式的旅行售貨員問題


對於給定的無向圖G,可以利用找圖G的最小生成樹的算法設計找近似最優的旅行售貨員迴路的算法。

void approxTSP (Graph g)

{

  1. 選擇g的任一頂點r;

  2. 用Prim算法找出帶權圖g的一棵以r為根的最小生成樹T;

  3. 前序遍歷樹T得到的頂點表L;

  4. 將r加到表L的末尾,按表L中頂點次序組成迴路H,作為計 算結果返回;

}

   

當費用函數滿足三角不等式時,算法找出的旅行售貨員迴路的費用不會超過最優旅行售貨員迴路費用的2倍

 

實現


/* 主題:近似算法——旅行售貨員問題

* 作者:chinazhangjie

* 郵箱:chinajiezhang@gmail.com

* 開發語言: C++

* 開發環境: Virsual Studio 2005

* 時間: 2010.12.06

*/

 

#include <iostream>

#include <vector>

#include <limits>

using namespace std ;

 

struct TreeNode

{

public:

    TreeNode (int nVertexIndexA = 0, int nVertexIndexB = 0, int nWeight = 0)

        : m_nVertexIndexA (nVertexIndexA),

        m_nVertexIndexB (nVertexIndexB),

        m_nWeight (nWeight)

    { }

public:

    int m_nVertexIndexA ;

    int m_nVertexIndexB ;

    int m_nWeight ;

} ;

 

class MST_Prim

{

public:

    MST_Prim ()

    {}

    MST_Prim (const vector<vector<int> >& vnGraph)

    {

        m_nvGraph = vnGraph ;

        m_nNodeCount = (int)m_nvGraph.size () ;

    }

    void SetData (const vector<vector<int> >& vnGraph)

    {

        m_nvGraph = vnGraph ;

        m_nNodeCount = (int)m_nvGraph.size () ;

    }

    //

    const vector<TreeNode>& GetMSTree () const

    {

        return m_tnMSTree ;

    }

    //

    const vector<vector<int> >& GetGraph () const

    {

        return    m_nvGraph ;

    }

    void DoPrim ()

    {

        // 是否被訪問標誌

        vector<bool> bFlag (m_nNodeCount, false) ;

        bFlag[0] = true ;

 

        int nMaxIndexA ;

        int nMaxIndexB ;

        int j = 0 ;

        while (j < m_nNodeCount - 1) {

            int nMaxWeight = numeric_limits<int>::max () ;

            // 找到當前最短路徑

            int i = 0 ;

            while (i < m_nNodeCount) {

                if (!bFlag[i]) {

                    ++ i ;

                    continue ;

                }

                for (int j = 0; j < m_nNodeCount; ++ j) {

                    if (!bFlag[j] && nMaxWeight > m_nvGraph[i][j]) {

                        nMaxWeight = m_nvGraph[i][j] ;

                        nMaxIndexA = i ;

                        nMaxIndexB = j ;

                    }

                }

                ++ i ;

            }

            bFlag[nMaxIndexB] = true ;

            m_tnMSTree.push_back (TreeNode(nMaxIndexA, nMaxIndexB, nMaxWeight)) ;

            ++ j ;

        }

        // 輸出結果

        /*for (vector<TreeNode>::const_iterator ite = m_tnMSTree.begin() ;

                ite != m_tnMSTree.end() ;

                ++ ite ) {

            cout << (*ite).m_nVertexIndexA << "->"

                << (*ite).m_nVertexIndexB << " : "

                << (*ite).m_nWeight << endl ;

        }*/

    }

 

private:

    vector<vector<int> > m_nvGraph ;    // 無向連通圖

    vector<TreeNode>    m_tnMSTree ;    // 最小生成樹

    int    m_nNodeCount ;

} ;

 

 

class AA_TSP

{

public:

    AA_TSP (const vector<vector<int> >& vnGraph)

    {

        m_mstPrim.SetData(vnGraph) ;

    }

    void Get_AA_Path ()

    {

        m_mstPrim.DoPrim () ;

        vector<TreeNode>        mstree = m_mstPrim.GetMSTree () ;

        vector<vector<int> >    graph = m_mstPrim.GetGraph() ;

        int iweight = 0 ;

        for (vector<TreeNode>::const_iterator ite = mstree.begin() ;

                ite != mstree.end() ;

                ++ ite ) {

            cout << (*ite).m_nVertexIndexA << "->"

                << (*ite).m_nVertexIndexB << " : "

                << (*ite).m_nWeight << endl ;

            iweight += (*ite).m_nWeight ;

        }    

        cout << mstree[mstree.size()-1].m_nVertexIndexB << "->"

            << mstree[0].m_nVertexIndexA << " : "

            << graph[mstree[0].m_nVertexIndexA][mstree[mstree.size()-1].m_nVertexIndexB]

            << endl ;

        iweight += graph[mstree[0].m_nVertexIndexA][mstree[mstree.size()-1].m_nVertexIndexB] ;

        cout << "Total weight: " << iweight  << endl ;

    }

private:

    MST_Prim    m_mstPrim ;

};

int main()

{

    const int cnNodeCount = 5 ;

    vector<vector<int> > graph (cnNodeCount) ;

    for (size_t i = 0; i < graph.size() ; ++ i) {

        graph[i].resize (cnNodeCount, numeric_limits<int>::max()) ;

    }

    graph[0][1] = 5 ;

    graph[0][2] = 1 ;

    graph[0][3] = 2 ;

    graph[0][4] = 3 ;

 

    graph[1][0] = 5 ;

    graph[1][2] = 4 ;

    graph[1][3] = 2 ;

    graph[1][4] = 2 ;

 

    graph[2][1] = 4 ;

    graph[2][0] = 1 ;

    graph[2][3] = 5 ;

    graph[2][4] = 3 ;

 

    graph[3][1] = 2 ;

    graph[3][2] = 5 ;

    graph[3][0] = 2 ;

    graph[3][4] = 2 ;

 

    graph[4][1] = 2 ;

    graph[4][2] = 3 ;

    graph[4][3] = 2 ;

    graph[4][0] = 3 ;

 

    AA_TSP aa (graph) ;

    aa.Get_AA_Path () ;

     return 0 ;

}


2  一般的旅行售貨員問題

在費用函數不一定滿足三角不等式的一般情況下,不存在具有常數性能比的解TSP問題的多項式時間近似算法,除非P=NP。換句話説,若P≠NP,則對任意常數ρ>1,不存在性能比為ρ的解旅行售貨員問題的多項式時間近似算法。


覺得本文有幫助?請分享給更多人

關注「算法愛好者」,修煉編程內功

閲讀原文

TAGS: