數據結構筆記:如何隨機生成有向無環圖


在驗證有向無環圖相關的各類算法時須要一些測試數據,手動構造的話太麻煩了,因而便想着能不能自動生成一些測試數據來。這個問題的難點在於如何保證生成的圖沒有環,查了一下相關資料,這個能夠藉助拓撲排序的原理來實現,想象一下一個有向無環圖要對其拓撲排序,須要從圖中找出一個入度爲0的頂點,將它和它的出邊都從圖中刪除,重複執行這個操做直到圖爲空,只須要逆向執行這個過程便可從拓撲排序的結果恢復出一個有向無環圖,好比下面這個有向無環圖:html

對其拓撲排序的結果是:java

a, b, c算法

那麼只須要隨機地將前面頂點鏈接到後面頂點便可將拓撲排序的結果還原爲有向無環圖,好比隨機地從a連向b或c,從b連向c,不過須要注意的是由於拓撲排序會丟失邊的信息,因此這個還原並不能保證和原圖一致。app


下面是針對這個原理寫的一個工具類,輸入拓撲排序,根據此拓撲排序生成隨機有向無環圖。dom

表示頂點和其鄰接關係的實體類:工具

package org.cc11001100.alg.graph.topologicalSorting.generateDAG;

import java.util.ArrayList;
import java.util.List;

/**
 * @author CC11001100
 */
public class Vector<T> {

    private T name;
    private List<Vector<T>> from;
    private List<Vector<T>> to;

    public Vector(T name) {
        this.name = name;
        this.from = new ArrayList<>();
        this.to = new ArrayList<>();
    }

    public T getName() {
        return name;
    }

    public void setName(T name) {
        this.name = name;
    }

    public List<Vector<T>> getFrom() {
        return from;
    }

    public List<Vector<T>> getTo() {
        return to;
    }
}

而後是根據拓撲排序生成DAG的工具類:測試

package org.cc11001100.alg.graph.topologicalSorting.generateDAG;

import java.util.List;
import java.util.Random;

/**
 * 生成隨機DAG
 *
 * @author CC11001100
 */
public class DAGGenerator {

    /**
     * 傳入一個拓撲排序好的頂點列表,而後從這個拓撲排序中隨機生成一個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.getTo().add(to);
                    to.getFrom().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.getFrom().isEmpty()) {
                Vector<T> from = topologicalSortedVectorList.get(random.nextInt(i));
                from.getTo().add(to);
                to.getFrom().add(from);
            }
        }
    }

}

爲了更直觀的觀察生成的結果,咱們將其繪製爲圖形,這裏使用dot language,IDEA能夠藉助PlatUML插件方便的渲染,這裏不是介紹工具或語言的用法的,若有興趣請自行查閱相關資料。ui

測試類,將生成結果轉換爲dot language以渲染:this

package org.cc11001100.alg.graph.topologicalSorting.generateDAG;

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<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.getName()).append(";\n"));
        sb.append("\n");
        vectorList.forEach(from -> from.getTo().forEach(to -> {
            sb.append("    ").append(from.getName()).append(" -> ").append(to.getName()).append(";\n");
        }));
        sb.append("}\n")
                .append("\n@enduml\n");
        return sb.toString();
    }

    public static void main(String[] args) {
        final int vectorCount = 10;
        List<Vector<Integer>> vectorList = new ArrayList<>(vectorCount);
        for (int i = 0; i < vectorCount; i++) {
            vectorList.add(new 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

查看渲染效果:

image

經過圖形渲染,可以更直觀的看到,生成的確實是一個有向無環圖,至此實現了測試數據能夠自動生成,下面的實驗能夠開心的繼續下去了。


.

相關文章
相關標籤/搜索