1. 程式人生 > >求解大規模有向圖的所有連通分量

求解大規模有向圖的所有連通分量


              一 超大規模的稀疏圖中的連通分量求解
背景
      某地圖廠商,需要檢查道路網是否是連通的,比如是否存在孤島(不能到達,也不能外出)?通常情況下,如果存在孤島,說明道路勘測有問題。因此,隨之而來的問題是:
(1)不考慮道路方向,是否所有道路都是連通的?
(2)如果考慮道路方向,是否任意2條道路均可以相互可達,如果不可以,輸出可達情況,如存在path(u,v),但是不存在path(v,u)。
   由於地圖道路資料十分龐大,因此需要一個快速的演算法/策略來實現目標,附加要求,記憶體不超過4G,時間4個小時,道路網,大約40M個點和40M條邊。
這裡先不假定這個附加要求的合理性,比如40m個點或者邊可能是分散式儲存。

 
無向圖
    表述:無向圖G(E,V)中尋找各連通分量
1. 基本演算法策略
    因為是無向圖,因此,BFS(廣度優先搜尋)或者DFS(深度優先搜尋)都可以用來求各連通分量,其演算法複雜度均為O(V+E)。但是,通常,大規模圖都是稀疏圖,DFS會佔用大量的儲存空間,因此使用BFS更加有效。
   A. BFS演算法
       這個基本上資料結構或演算法基礎類的書籍都有,因此不闡述。注意,雖然DFS也是可以的,但是,遍歷過程中會產生一很深的棧,無論是時間還是空間效率考慮,這演算法是不合適的。
  B. 等價關係演算法
       等價類的劃分,需要使用一種叫做disjoint set的資料結構。一條邊E(u,v)相當於u和v存在等價關係,即滿足自反(self-clousure)、對稱sysmetry,傳遞(transitivity)。因此,可以使用disjoint set結構來實現。演算法複雜度O(ma(n))。 Ackerman是一個增長極為緩慢的函式,因此也可以認定複雜度也是O(V+E)。
  C. 效能比較
      BFS略優於Disjoint-set等價關係演算法
最終,採用的是等價關係演算法。理由:等價關係演算法是個online演算法,可以任意增加一條邊,而不需要重新構造圖G;不需要遍歷vertex;適合分散式處理。

 
有向圖

     有向圖的計算,實現難度要比無向圖高很多。主要存在空間限制。為此,採用Divide & conquer思想來設計。很類似於分散式中的Map Reduce概念。
     基本原理類同Map & Reduce,但是需要注意的:
A. 單機多執行緒並不能提高執行速度。實際上處理速度很快,主要瓶頸在儲存空間限制。因此提高IO效能能夠顯著提高計算效率。
B. 主要限制在記憶體。因此需要使用外存來輔助進行歸併。
1. 演算法步驟
步驟:分為3個階段:劃分子圖、計運算元圖、歸併子圖
(1) 劃分子圖(Partition Phase)
     將資料分成各個partition,每個partition是一個獨立的子圖。
這裡需要一個先驗性的假設,劃分子圖能夠較好的內聚性,即相鄰的vertex劃分後大多聚集在一個table中。
不能像普通的map-reduce的按照隨機原則來劃分。

     在道路網狀中,最好的方式是通過經緯度座標來實現聚類劃分。具體實現方式可以採用網格索引(速度最快,簡單明瞭,但可能不夠均勻和平衡)。
     實際上,地圖廠商都是按照省,或者城市來處理的,比如均勻網格劃分的。因此,每個省作為一個子圖是很合適,一個省的地圖資料量完全可以在4G記憶體中完成。
     圖的劃分用2種方式:Split Vertex 和Split Edges。
     這裡劃分是邊劃分即Split Edges,即不切割頂點,只切割與頂點相關的邊。
需要將所有切割的邊儲存,在後面的歸併階段需要使用。
(2) 計運算元圖(Do Phase)
     <1> 求連通分量,執行SCC演算法。——Robert Tarjan的強連通分量演算法
            計算完子圖的連通性後,需要儲存各子圖以及該子圖連通關係。
由於記憶體限制,每個子圖通常需要儲存為檔案file(i)。用subgraph(i)表示相應第i個子圖, scc(i,j),表示第i個子圖,第j個強連通分量。
    <2> 構建新坍縮子圖
           第i個子圖對應的新的坍縮子圖記為collapse_subgraph(i)。通常該子圖也儲存為檔案。
           A. 頂點集合。對改圖的第j個連通分量Scc(j),使用該連通分量某個頂點(通常是Tranjan演算法中,深度優先搜尋生成樹的根Root),記為root(u),作為新子圖collpase_subgraph(i)的頂點集合。即原始頂點u所在連通分量都使用同一個頂點root(u)代替。
           B. 邊集合。新增各連通分量之間的邊(從原始子圖subgraph(i)中獲得)到新collpase_subgraph(i)中,不需要新增重複邊,如連通分量A,可以通過頂點a1,a2到達連通分量B,但從A到B僅需要新增一條邊。
           C. Collapse_subgraph實際上是個DAG圖。當然可能存在獨立的相互間都不可達的子圖。這種容易出現在劃分邊界處。
(3) 歸併子圖 (Reduce)
     歸約過程也是一個合併圖,並求其scc的過程。
    以歸約2個子圖subgraph(i)和subgraph(j)為例: 
    找到步驟(2)子圖i、j,subgraph(i),subgraph(j)對應的坍縮子圖collapse(i),collapse(j)。
建立一個新的歸併圖merge_graph,將collapse(i),collapse(j)作為mergegraph的子圖。(即新增其所有頂點和邊到新的merge_graph中)。
         <1> 對所有在步驟(1)中劃分的邊E(u,v),該邊u、v滿足分別屬於兩個子圖i,j。
root(u), root(v),新增邊E’(root(u),root(v)到新的merge_graph。同樣,不需要新增重複邊。
此時生成的是一個新的歸併mergegraph。
通常是2個邏輯上相鄰的子圖進行歸併,兩兩歸併。因此,假設當前有N個子圖,本趟歸併需要[N/2 ]次。
(4) 迴圈
對步驟3生成的每個歸併圖merge_graph,重複步驟(2)、步驟(3),直到所有臨時結果都已經歸併。
2. 注意事項
在步驟2,3中,
第i個子圖subggraph(i)中所有關聯的頂點可以通過檔案file(i)得到,但要查詢某個頂點所在的子圖(步驟2),需要使用hash表或者資料庫。由於記憶體限制,最好使用資料庫,如果使用檔案,也需要單獨對其建立索引,否則該步查詢效率低,特別是頻繁查詢兩個子圖連線的分割邊e(u,v),u,v屬於兩個不同的子圖。另外,每次歸併都需要儲存臨時結果。
由於I/O是程式瓶頸,需要建立合適的索引來檢索頂點、邊是,以減少整個程式的執行時間。
3. 演算法分析
      演算法非常類似與排序演算法的歸併演算法,因此,總歸併次數為N-1次。每次求連通分量時間複雜度O(V+E),空間複雜度O(V)。演算法效能,非常依賴於圖的劃分,由於涉及到的是地圖,道路通常是連線地理上相鄰的點,因此,按照座標區域即網格劃分是適合的。(Graph partition,這是另外一個研究課題,參考文獻豐富,如A FAST AND HIGH QUALITY MULTILEVEL SCHEME FOR PARTITIONING IRREGULAR GRAPHS   GEORGE KARYPIS  AND VIPIN KUMAR) 
    最壞情況下,整個網路,是一個無環有向圖(DAG),或者每次劃分都不能有效的縮減求解規模。
4. 實際測試結果分析
在真實的地圖中,道路圖,是一個條件有向圖,不能使用通常的G=(V,E)來表示。舉例如下, L1經過C點,可通過L2到達L8,但是不能同L2到達L4,但是L0可經過點C,通過L2,左轉到L4也可以直行到L8。
此時<L1,L2,L4>構成一個禁止轉向邊——條件邊,它不同於相鄰邊的拓撲關係描述,如簡單的靜止左轉或者右轉,如圖中L2到L4,是可以通過對偶圖來去掉條件邊的。對偶圖轉換,簡單的看,是把原圖中的邊當成新圖中頂點,原圖中邊與邊的關係,作為新圖中的邊。

還有屬性邊,比如限重門,限寬、高門,時間域等。但這些與設計的演算法思路無關。

以北京地圖為例,15萬個頂點,17萬條邊,大約29s完成scc求解(包含結果作為檔案輸出)。