我有两个加权DAG(有向无环图),需要将它们合并为一个,因此我可以进行拓扑排序(某些情况下可以超过两个)。 问题在于,每个图都是非循环的,但可以一起形成一个循环。 而且,这些图很大(100k +个节点,500k +个边)。
有没有巧妙的方法来合并图形? 同样好的算法可以"一次"遍历所有图形。
编辑:
"合并"是指将两个图形的所有边和顶点合并在一起(当然会保留权重)(如果它们不创建循环)。 如果边缘已经存在,我想使用更大的权重。
这个想法是,从两个非循环图开始应该比随后简单地"固定"结果具有优势(这意味着找到难于NP的反馈弧集,所以我想避免这种情况)。
谢谢您的建议。
合并是什么意思? 请对此进行更具体的数学说明
感谢您的回答。 我修改了问题以澄清。
创建一个循环时仍不清楚该怎么做。
一个问题是可能有不止一种解决方案。
例如,考虑DAG {(a,b),(a,c)}和{(b,a),(b,c)}。您可以通过两种不同的方式"合并"这些:
{(a,b),(a,c),(b,c)}
{(a,c),(b,a),(b,c)}
解决方案的数量与两个图的并集中的循环数成正比地增长,因此对于大图,可能有很多"合并"它们的方式。
您是否有一个标准可以帮助您确定哪个DAG比另一个更好?
我有一个类似的问题,我这样解决了:
我将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 {}))})
假设两个图的顶点相同,如果不同,
考虑一下
V = V1 U V1
假设您有一个邻接表。现在,对于V1和V2中的每个顶点v,您都可以按边缘导致的顶点对邻接表进行排序(如果是(顶点,权重)对,则按顶点排序)。因为图形很小,所以它应该不那么昂贵,并且应该是summation degree(v)*log(degree(v)),它应该还不错。
之后,您可以迭代V1和V2中的所有顶点v,并对v1和V2中的v邻接列表进行合并排序。这类似于使用合并排序查找2个排序的数组的并集,只有找到两个元素都出现的元素时,您才可以选择较大的边。