是否有用于在图中查找冗余边的既定算法?
例如,我想找到a-> d和a-> e是多余的,然后摆脱它们,如下所示:
=>
编辑:Strilanc非常善于为我读懂我的想法。 “冗余”太强了,因为在上面的例子中,a-> b或a-> c都不被认为是多余的,但a-> d是。
答案 0 :(得分:26)
您想要计算维持顶点可达性的最小图。
这称为图表的transitive reduction。维基百科文章应该让你开始走正确的道路。
答案 1 :(得分:1)
给定图形的不包含“冗余边缘”的子图称为该图的“spanning tree”。对于任何给定的图形,可以使用多个生成树。
因此,为了摆脱冗余边缘,您需要做的就是找到图形的任何一个生成树。您可以使用任何depth-first-search或breadth-first-search算法并继续搜索,直到您访问了图表中的每个顶点。
答案 2 :(得分:1)
答案 3 :(得分:1)
有几种攻击方法,但首先你需要更准确地定义问题。首先,你在这里的图表是非循环的和有针对性的:这总是真的吗?
接下来,您需要定义“冗余边缘”的含义。在这种情况下,您从一个图表开始,该图表有两条路径a-> c:一条经由b而另一条是直接的。由此我推断,通过“冗余”,你的意思是这样的。设 G =< V,E> 是一个图形, V 顶点的集合和E⊆V×V 边缘集合。有点看起来你要定义从 v i 到 v j 的所有边缘都比最长边缘短“冗余”。所以最简单的方法是使用深度优先搜索,枚举路径,当你找到一个更长的新路径时,将其保存为最佳候选者。
但是,我无法想象你想要它做什么。你能告诉?答案 4 :(得分:0)
我遇到了类似的问题,最终以这种方式解决了问题:
我的数据结构由dependends
字典组成,从节点id到依赖它的节点列表(即DAG中的跟随者)。请注意,它仅适用于DAG - 即有向,非循环图。
我还没有计算出它的确切复杂程度,但它在瞬间吞噬了几千个图表。
_transitive_closure_cache = {}
def transitive_closure(self, node_id):
"""returns a set of all the nodes (ids) reachable from given node(_id)"""
global _transitive_closure_cache
if node_id in _transitive_closure_cache:
return _transitive_closure_cache[node_id]
c = set(d.id for d in dependents[node_id])
for d in dependents[node_id]:
c.update(transitive_closure(d.id)) # for the non-pythonists - update is update self to Union result
_transitive_closure_cache[node_id] = c
return c
def can_reduce(self, source_id, dest_id):
"""returns True if the edge (source_id, dest_id) is redundant (can reach from source_id to dest_id without it)"""
for d in dependents[source_id]:
if d.id == dest_id:
continue
if dest_id in transitive_closure(d.id):
return True # the dest node can be reached in a less direct path, then this link is redundant
return False
# Reduce redundant edges:
for node in nodes:
dependents[node.id] = [d for d in dependents[node.id] if not can_reduce(node.id, d.id)]
答案 5 :(得分:0)
由于@Craig提到的Wikipedia文章仅对实现起到了推波助澜的作用,因此我将实现与Java 8流一起发布:
Map<String, Set<String>> reduction = usages.entrySet().stream()
.collect(toMap(
Entry::getKey,
(Entry<String, Set<String>> entry) -> {
String start = entry.getKey();
Set<String> neighbours = entry.getValue();
Set<String> visited = new HashSet<>();
Queue<String> queue = new LinkedList<>(neighbours);
while (!queue.isEmpty()) {
String node = queue.remove();
usages.getOrDefault(node, emptySet()).forEach(next -> {
if (next.equals(start)) {
throw new RuntimeException("Cycle detected!");
}
if (visited.add(next)) {
queue.add(next);
}
});
}
return neighbours.stream()
.filter(s -> !visited.contains(s))
.collect(toSet());
}
));
答案 6 :(得分:-1)
我认为最简单的方法,实际想象一下它在实际工作中的表现,想象一下你是否有关节,喜欢
(A-> B)(B-> C)(A-> C),想象一下近图之间的距离是否等于1,所以
(A-> B)= 1,(B-> C)= 1,(A-> C)= 2.
所以你可以移除关节(A-> C)。
换句话说,最小化。
这只是我的想法,我将在开始时考虑它。网上有各种文章和资料来源,你可以看看它们并深入了解。
资源,可以帮助您:
Algorithm for Removing Redundant Edges in the Dual Graph of a Non-Binary CSP
Graph Data Structure and Basic Graph Algorithms
Google Books, On finding minimal two connected Subgraphs
Redundant trees for preplanned recovery in arbitraryvertex-redundant or edge-redundant graphs