最近在研究圖計算的性能,須要構造不一樣的測試數據對圖算法進行壓測,其中就涉及到均勻圖的概念。java
由於作的是理論測試,所以就須要一種理論上絕對均勻的圖測試數據,接下來咱們就討論一下絕對均勻圖的生成。git
爲了方便討論,咱們只討論無向圖,並且圖中的邊是無權值的,且兩點之間只能存在一條邊,即邊僅表明結點之間的關聯。github
從圖論角度出發,咱們都知道圖都是由結點以及結點之間的關聯邊組成的。直觀上理解,絕對均勻的圖應該是圖中的全部結點的度都徹底相同,這樣每一個結點都是同構的,也就是說從任何一個結點進行觀察,獲得的都是一樣的結果。算法
形式化的描述應該是這樣,對於圖 \(G=(V, E)\) ,其中\(V\)是結點集合,\(E\)是邊集合,記 \(|V|\) 爲結點數, \(|E|\) 爲邊數,圖的平均度爲 \(D\) ,因爲每條邊爲圖貢獻兩個度,因而存在如下關係:
$
D = \frac{2\cdot|E|}{|V|}(公式1)
$框架
所以,根據前面對均勻概念的理解,對於絕對均勻圖,任何一個節點的度數都知足:
$
d_v = D = \frac{2\cdot|E|}{|V|}(公式2)
$性能
明確了絕對均勻圖的概念後,接下來就是如何生成的問題。因爲
$
|E| = \frac{d_v\cdot|V|}{2}(公式3)
$
咱們只須要控制結點數 \(|V|\) 和每一個結點的度數 \(d_v\) 這兩個變量便可。所以咱們的目標就是生成任意結點數,且結點度數任意的絕對均勻圖。測試
咱們知道,徹底圖其實就是一種絕對均勻圖,其全部節點的度數爲 \(|V| - 1\) ,這已是圖中結點能夠達到的最高度數了。相應的,空圖(只有點,沒有邊)也是絕對均勻圖,即全部節點的度數都爲 \(0\),爲最低度數。所以絕對均勻圖的結點度數老是知足:
$
0\le d_v\lt|V|(公式4)
$spa
經過觀察上面的公式2,咱們還能夠得出以下結論:3d
一言以蔽之,對於奇數點數的絕對均勻圖,結點度數只能取 \([0, |V|)\) 以內的偶數。故而在圖生成算法上須要對奇數點數圖區分對待。code
那麼如何構建絕對均勻圖呢?首先比較容易想到的就是遞歸思想,遞歸的基本思路是:
上面討論過,對於絕對均勻圖,有兩個變量影響圖的規模:結點數 \(|V|\) 和結點度數 \(d_v\) 。所以分析時,要假定其中有一個不變量才比較好思考。
因爲奇數結點數的圖的結點度數不可能爲奇數,所以咱們保持結點度數爲偶數不變,保證結點數從奇數到偶數再到奇數時,能夠連續地推導問題變化規律。
這裏取\(d_v = 2(0度不具有參考性,可做爲邊界條件考慮)\),令 \(|V| = 3, 4, 5, 6\),能夠獲得以下圖實例。
貌似按照環形圖的思考方式,問題是能夠遞歸的。每增長一個結點時,只須要選取一條邊斷開,而後鏈接到新的結點便可。
再考慮一下\(d_v = 4\)的狀況,令 \(|V| = 5, 6\),能夠獲得以下圖實例。
這種狀況下,好像使用上述的遞歸擴展方式就不太容易了。那另一種狀況呢?
這裏取 \(|V| = 6\),令 \(d_v = 0, 1, 2, 3, 4, 5, 6\),能夠獲得以下圖實例。
能夠看到,當度從\(3\)增加到\(4\)時,新規模問題的解並不包含上一級問題全部的解,並且每一級變化的規律並不穩定。
所以用上面的遞歸思想去分析絕對均勻圖生成的問題可能並不方便,咱們須要轉換一下思路。
回到第一節對絕對均勻圖的概念描述:絕對均勻圖的結點是同構的,知足各向同性。這就意味着咱們能夠從一個結點出發,去設法尋找它的關聯節點,而且這種方式對任何一個結點都是相同的。
那麼如何找到關聯結點呢?考慮到絕對均勻圖每一個點都是同構的,所以絕對均勻圖必定是中心對稱的!假設這個虛擬的對稱中心爲\(O\),在\(d_v = 1\)的狀況下,關聯結點必定是當前結點的中心對稱結點。
那在\(d_v = 2\)的狀況下呢?
或許會有人好奇,這個\(d_v = 2\)的圖怎麼不是環呢?其實對於一樣結點數和度數的絕對均勻圖,圖的結構可能不止一種。
按照中心對稱思想依此類推,能夠獲得其餘度的絕對均勻圖。
所以,關聯結點的尋找思路以下:
咱們發現,前面討論的不可行的遞歸方式主要是被環形圖的思路干擾了。利用中心對稱的關聯結點尋找方式是也是能夠遞歸的,不過使用循環實現可能更方便。
基於中心對稱思想的絕對均勻圖生成算法實現以下:
public boolean generate(int count, int degree) { // 基本參數校驗 if (!(count > 0 && degree >= 0 && degree < count)) { System.err.println("Invalid arguments !"); return false; } // 檢查奇數點圖的度是不是偶數 if (count % 2 == 1 && degree % 2 != 0) { System.err.println("Degree should be odd when vertices' count is even !"); return false; } // 生成點 System.out.println("Generating vertices:"); for (int index = 0; index < count; index++) { System.out.println(index); } // 生成邊 System.out.println("Generating edges:"); for (int index = 0; index < count; index++) { // 對面的點 int opposite = (index + count / 2) % count; // 點偶數,度奇數,連接中心對稱結點 if (count % 2 == 0 && degree % 2 == 1) { System.out.println(index + " -> " + opposite); } // 連接中心對稱結點兩側的點 for (int i = (count + 1) % 2; i <= (degree - count % 2) / 2; i++) { final int previous = (opposite - i + count) % count; final int next = (opposite + i + count % 2) % count; System.out.println(index + " -> " + previous); System.out.println(index + " -> " + next); } } return true; }
爲了更直觀的展現生成的絕對均勻圖,能夠藉助vis.js庫進行繪製。
具體實現方式能夠訪問github源碼drawG,該項目實現了一個簡單的圖生成與繪製框架,能夠方便定製和擴展圖生成器和處理器。
最後,看一下使用該框架生成的絕對均勻圖: