[英]Finding all the paths between two vertices with weight limit in a directed graph
我试图在可能有循环但没有自循环的有向加权图中找到两个顶点之间的所有路径,这些顶点的权重小于N。 到目前为止,我只能通过使用AllDirectedPaths
来做到这一点,然后过滤掉权重大于 N 的路径:
SimpleDirectedWeightedGraph<String, DefaultWeightedEdge> g = new SimpleDirectedWeightedGraph<>(DefaultWeightedEdge.class);
AllDirectedPaths<String, DefaultWeightedEdge> allPaths = new AllDirectedPaths<>(g);
int maxWeight = 30;
List<GraphPath<String, DefaultWeightedEdge>> pathsLimitedByWeight = allPaths.getAllPaths(startVertex, endVertex, false, maxWeight / minGraphLatency)
.filter(graphPath -> graphPath.getWeight() < maxWeight)
.collect(Collectors.toList());
这种方法显然不是最理想的,因为对于较大的图它很慢。 为了限制 output 并使其更快,我将maxPathLength
提供给AllDirectedPaths#getAllPaths
,我将其设置为路径可以具有的最大权重除以图中边的最小权重,因为在我的情况下边权重是 integer 和总是大于 1。
我考虑将KShortestSimplePaths
与自定义PathValidator
一起使用,但它仅支持简单路径,即不允许循环。
如果有的话,我在 jgrapht 中有什么其他选项可以用来解决查找所有路径而不必自己遍历图形。
没有一种算法可以让您有效地查询一对顶点之间的所有非简单路径。 可以有成倍增长的路径。 想象一个具有以下边的图:(s,u),(u,v),(v,u),(u,t),其中所有边的长度为 1。现在找到从 s 到 t 的所有非简单路径,重量限制为 N。您将获得以下路径:
你可以继续循环 [u,v,u] 直到你最终达到体重限制。 如果这确实是您想要的,我建议您实施一个简单的标签算法。 label 对部分路径进行编码。 A label keeps a reference to its preceding label, a reference to the node the label is associated with, as well as a cost equal to the total cost of the partial path represented by the label. 通过为成本为 0 的源节点 s 创建 label 来启动算法,并将其添加到开放标签队列中。 在算法的每次迭代期间,从打开的队列中轮询 label,直到队列耗尽。 For a polled label L associated with node i and having cost c, expand the label: for each neighbor j of node i, create a new label L' that points back to label L and set its cost equal to c plus edge weight d_ij. 如果新的 label L' 的成本超过可用预算,则丢弃 label。 否则,如果 j 是目标节点,我们找到了一个新路径,所以存储 label 以便我们以后可以恢复路径。 否则,将 L' 添加到打开标签的队列中。 可以在下面找到该算法的简单实现。
笔记:
import org.jgrapht.*;
import org.jgrapht.graph.*;
import java.util.*;
public class NonSimplePaths<V,E> {
public List<GraphPath<V, E>> computeNoneSimplePaths(Graph<V,E> graph, V source, V target, double budget){
GraphTests.requireDirected(graph); //Require input graph to be directed
if(source.equals(target))
return Collections.emptyList();
Label start = new Label(null, source, 0);
Queue<Label> openQueue = new LinkedList<>(); //List of open labels
List<Label> targetLabels = new LinkedList<>(); //Labels associated with target node
openQueue.add(start);
while(!openQueue.isEmpty()){
Label openLabel = openQueue.poll();
for(E e : graph.outgoingEdgesOf(openLabel.node)){
double weight = graph.getEdgeWeight(e);
V neighbor = Graphs.getOppositeVertex(graph, e, openLabel.node);
//Check whether extension is possible
if(openLabel.cost + weight <= budget){
Label label = new Label(openLabel, neighbor, openLabel.cost + weight); //Create new label
if(neighbor.equals(target)) //Found a new path from source to target
targetLabels.add(label);
else //Must continue extending the path until a complete path is found
openQueue.add(label);
}
}
}
//Every label in the targetLabels list corresponds to a unique path. Recreate paths by backtracking labels
List<GraphPath<V,E>> paths = new ArrayList<>(targetLabels.size());
for(Label label : targetLabels){ //Iterate over every path
List<V> path = new LinkedList<>();
double pathWeight = label.cost;
do{
path.add(label.node);
label=label.pred;
}while(label != null);
Collections.reverse(path); //By backtracking the labels, we recoved the path in reverse order
paths.add(new GraphWalk<>(graph, path, pathWeight));
}
return paths;
}
private final class Label{
private final Label pred;
private final V node;
private final double cost;
private Label(Label pred, V node, double cost) {
this.pred = pred;
this.node = node;
this.cost = cost;
}
}
public static void main(String[] args){
Graph<String,DefaultWeightedEdge> graph = new SimpleDirectedWeightedGraph<>(DefaultWeightedEdge.class);
Graphs.addAllVertices(graph, Arrays.asList("s","u","v","t"));
graph.addEdge("s","u");
graph.addEdge("u","t");
graph.addEdge("u","v");
graph.addEdge("v","u");
graph.edgeSet().forEach(e -> graph.setEdgeWeight(e,1.0)); //Set weight of all edges to 1
NonSimplePaths<String,DefaultWeightedEdge> nonSimplePaths = new NonSimplePaths<>();
List<GraphPath<String,DefaultWeightedEdge>> paths = nonSimplePaths.computeNoneSimplePaths(graph, "s", "t", 10);
for(GraphPath<String,DefaultWeightedEdge> path : paths)
System.out.println(path+" cost: "+path.getWeight());
}
}
上述示例代码的Output:
[s, u, t] cost: 2.0
[s, u, v, u, t] cost: 4.0
[s, u, v, u, v, u, t] cost: 6.0
[s, u, v, u, v, u, v, u, t] cost: 8.0
[s, u, v, u, v, u, v, u, v, u, t] cost: 10.0
提高上述实现的性能,例如通过添加一个可接受的启发式,我将作为练习留给 OP。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.