用于合并两个DAG的高​​效算法

时间:2010-12-19 12:08:44

标签: algorithm graph directed-acyclic-graphs

我有两个加权DAG(有向非循环图)并需要将它们合并为一个,所以我可以得到一个拓扑排序(在某些情况下它可能超过两个)。问题是图表各自都是非循环的,但可以一起形成一个循环。此外,图形很大(100k +节点,500k +边缘)。 有没有一种聪明的方法来合并图表?同样好的将是一次“一次”遍历所有图形的算法。

编辑:

通过“合并”我的意思是将两个图形的所有边和顶点组合在一起(当然保留权重),如果它们不创建周期的话。如果边缘已经存在,我想为它使用更大的权重。

这个想法是从两个非循环图开始应该比以后简单地“修复”结果更有优势(这意味着找到NP难以反馈的弧集,所以我想避免这种情况)。

感谢您的建议。

3 个答案:

答案 0 :(得分:2)

一个问题是可能存在多个解决方案。

例如考虑DAG {(a,b),(a,c)}和{(b,a),(b,c)}。您可以通过两种不同的方式“合并”这些:

  1. {(A,B),(A,C),(B,C)}
  2. {(A,C),(B,A),(B,C)}
  3. 解决方案的数量与两个图形的并集中的周期数组合增长,因此对于大图,可能有很多方法可以“合并”它们。

    您是否有一个标准可以帮助您确定哪个DAG比另一个“更好”?

答案 1 :(得分:0)

假设两个图形的顶点相同,如果没有, 只考虑

V = V1 U V1

让我们假设您有一个邻接列表。现在对于V1和V2中的每个顶点v,您可以按边缘所指向的顶点对邻接列表进行排序(如果是(顶点,权重)对,则按顶点排序)。这应该不是那么昂贵,因为图表很小,而且summation degree(v)*log(degree(v))不应该那么糟糕。

在此之后,您可以迭代V1和V2中的所有顶点v,并在V1和V2中对v的邻接列表进行合并排序。这类似于使用合并排序查找2个排序数组的并集,只有在您可以选择更大边缘的情况下才能找到元素的位置。

答案 2 :(得分:0)

我有类似的问题,我这样解决了:

我将DAG转换为带有节点映射的地图(由节点id键入,值为节点集合,可能是一个开始)和边缘映射(由源,目标对键入,值是一个集合边缘,可能是一个开始)。我称之为正常化。原始图表是"根"的集合。每个节点都有一个子集合

然后我可以通过按键和按键合并节点来合并其中的两个。即:如果某个节点确实存在,则将新节点包含在现有节点值中,如果某个节点不存在,则添加该节点。与边缘相同。

这很干净,但它没有避免循环。

以下是我使用的一些代码(clojure):

(def empty-graph
   {:nodes {}
    :edges {}})

(defn merge-graphs
  [a b]
  {:nodes (merge-with concat (get a :nodes) (get b :nodes))
   :edges (merge-with concat (get a :edges) (get b :edges))})

(defn normalize-graph
  [graph]
  {:nodes (->>
            graph
            (mapcat
              (fn [root]
                (->>
                  root
                  (tree-seq (fn [_] true) (fn [node] (get node :children)))
                  (mapcat
                    (fn [edge]
                      (concat
                        (if-let [source (get edge "source")]
                          [[source [source]]]
                          [])
                        (if-let [target (get edge "target")]
                          [[target [target]]]
                          [])))))))
            (into {}))
   :edges (->>
            graph
            (mapcat
              (fn [root]
                (->>
                  root
                  (tree-seq (fn [_] true) (fn [node] (get node :children)))
                  (filter (fn [edge] (and (not (nil? (get edge "source"))) (not (nil? (get edge "target")))))) ;this sucks but is necessary
                  (map
                    (fn [edge]
                      (let [compact (dissoc edge :children)]
                        [[(get edge "source") (get edge "target")] [compact]]))))))
            (into {}))})