數據結構筆記:如何生成隨機有向無環圖
阿新 • • 發佈:2019-02-02
args pri 過程 語言 mage rul 逆向 無環 丟失
在驗證有向無環圖相關的各種算法時需要一些測試數據,手動構造的話太麻煩了,於是便想著能不能自動生成一些測試數據來。查了一下相關資料,這個可以借助拓撲排序的原理來實現,想象一下一個有向無環圖要對其拓撲排序,需要從圖中找出一個入度為0的頂點,將它和它的出邊都從圖中刪除,重復執行這個操作直到圖為空,只需要逆向執行這個過程即可從拓撲排序的結果恢復出一個有向無環圖,比如下面這個有向無環圖:
對其拓撲排序的結果是:
a, b, c
那麽只需要隨機地將前面頂點連接到後面頂點即可將拓撲排序的結果還原為有向無環圖,比如隨機地從a連向b或c,從b連向c,不過需要註意的是因為拓撲排序會丟失邊的信息,所以這個還原並不能保證和原圖一致。
下面是針對這個原理寫的一個工具類,輸入拓撲排序,根據此拓撲排序生成隨機有向無環圖:
package org.cc11001100.alg.graph.topologicalSorting.generationDAG; import java.util.ArrayList; import java.util.List; import java.util.Random; /** * 生成隨機DAG * * @author CC11001100 */ public class DAGGenerator { public static class Vector<T> { T name; List<Vector<T>> from; List<Vector<T>> to; public Vector(T name) { this.name = name; this.from = new ArrayList<>(); this.to = new ArrayList<>(); } } /** * 傳入一個拓撲排序好的頂點列表,然後從這個拓撲排序中隨機生成一個DAG * * @param topologicalSortedVectorList * @return */ public static <T> void random(List<Vector<T>> topologicalSortedVectorList) { for (int i = 0, end = topologicalSortedVectorList.size(); i < end; i++) { for (int j = i + 1; j < end; j++) { if (Math.random() < 0.5) { Vector<T> from = topologicalSortedVectorList.get(i); Vector<T> to = topologicalSortedVectorList.get(j); from.to.add(to); to.from.add(from); } } } // 檢查是否有除了第一個之外入度為0的邊,如果有的話就從前面的頂點中隨機選一個連過來,這個是為了避免有獨立的頂點存在 Random random = new Random(); for (int i = 1, end = topologicalSortedVectorList.size(); i < end; i++) { Vector<T> to = topologicalSortedVectorList.get(i); if (to.from.isEmpty()) { Vector<T> from = topologicalSortedVectorList.get(random.nextInt(i)); from.to.add(to); to.from.add(from); } } } }
為了更直觀的觀察生成的結果,我們將其繪制為圖形,這裏使用dot language,IDEA可以借助PlatUML插件方便的渲染,這裏不是介紹工具或語言的用法的,如有興趣請自行查閱相關資料。
測試類,將生成結果轉換為dot language以渲染:
package org.cc11001100.alg.graph.topologicalSorting.generationDAG; import java.util.ArrayList; import java.util.List; /** * @author CC11001100 */ public class TestDAGGenerator { /** * FORMAT: * * <pre> * digraph abc{ * a; * b; * c; * d; * * a -> b; * b -> d; * c -> d; * } * </pre> * * @param graphName * @param vectorList */ public static <T> String convertToDot(String graphName, List<DAGGenerator.Vector<T>> vectorList) { StringBuilder sb = new StringBuilder(); sb.append("@startuml\n\n") .append("digraph ").append(graphName).append(" { \n"); vectorList.forEach(vector -> sb.append(" ").append(vector.name).append(";\n")); sb.append("\n"); vectorList.forEach(from -> from.to.forEach(to -> { sb.append(" ").append(from.name).append(" -> ").append(to.name).append(";\n"); })); sb.append("}\n") .append("\n@enduml\n"); return sb.toString(); } public static void main(String[] args) { final int vectorCount = 10; List<DAGGenerator.Vector<Integer>> vectorList = new ArrayList<>(vectorCount); for (int i = 0; i < vectorCount; i++) { vectorList.add(new DAGGenerator.Vector<>(i)); } DAGGenerator.random(vectorList); String dotGraph = convertToDot("test_DAG_generator", vectorList); System.out.println(dotGraph); } }
新建.puml文件,將上面的輸出粘貼進去:
@startuml digraph test_DAG_generator { 0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 0 -> 3; 0 -> 6; 0 -> 9; 0 -> 1; 1 -> 2; 1 -> 4; 1 -> 9; 2 -> 4; 2 -> 8; 2 -> 9; 3 -> 4; 3 -> 5; 3 -> 6; 4 -> 5; 4 -> 6; 4 -> 7; 5 -> 7; 5 -> 8; 6 -> 7; 6 -> 8; 7 -> 9; } @enduml
查看渲染效果:
通過圖形渲染,能夠更直觀的看到,生成的確實是一個有向無環圖,至此實現了測試數據可以自動生成,下面的實驗可以開心的繼續下去了。
.
數據結構筆記:如何生成隨機有向無環圖