当前位置: 首页 > 知识库问答 >
问题:

基于拓扑排序的有向无环依赖图并发处理分组任务

公孙黎昕
2023-03-14

我有一个任务类,它在执行前依赖于其他任务。我想将可以并行化的任务分组并排序。我决定它可以首先表示为DAG,并尝试使用JGrapht。首先,我遍历任务的输入列表,以获取所有具有依赖关系的任务,并将它们收集在一个列表中。然后,对于每个任务,我在图中创建一个顶点。

DirectedAcyclicGraph<Task, DefaultEdge> d = new DirectedAcyclicGraph<>(DefaultEdge.class);
Set<Task> all = collectAllTasks(tasks);
all.forEach(d::addVertex);

然后使用相同的列表,我试图创建节点之间的边。

all.forEach(task -> {
        Set<TaskName> predecessors = task.getPredecessors();

        predecessors.forEach(name -> {
            Task predecessor = taskService.getTaskByName(name);
            d.addEdge(predecessor, task);
        });

    });

然后我试着把任务分组

private Set<Set<TaskId>> groupTasks(DirectedAcyclicGraph<TaskId, DefaultEdge> dag) {
    Set<Set<TaskId>> groups = new LinkedHashSet<>();
    Iterator<TaskId> iterator = new TopologicalOrderIterator<>(dag);

    iterator.forEachRemaining(taskId -> {
        //source?
        if (dag.incomingEdgesOf(taskId).isEmpty()) {
            if (groups.isEmpty()) {
                Set<TaskId> set = new HashSet<>();
                set.add(taskId);
                groups.add(set);
            } else {
                groups.iterator().next().add(taskId);
            }
        }

        Set<TaskId> tasks = new HashSet<>(Graphs.successorListOf(dag, taskId));

        //sink?
        if (tasks.isEmpty()) {
            return;
        }

        groups.add(featuresGroup);
    });

    return groups;
}

因此,结果是有序和分组的任务,例如图

结果将是A, B,{C, D}, E.

然而,当B也是E的前身时,它就完全打破了这个例子

对于像上一个这样的图,如何实现A,B,{C,D},E?有没有什么特别的算法我可以看看,或者我如何能以更好的方式实现这一点?谢谢

共有1个答案

刁跃
2023-03-14

可使用以下程序获得溶液:

  1. 第一组包含所有没有传入弧的任务:这些任务没有传入依赖项

使用JGraphT:

public static List<List<String>> getGroups(Graph<String, DefaultEdge> taskGraph){
    List<List<String>> groups = new ArrayList<>();
    //The first group contains all vertices without incoming arcs
    List<String> group = new LinkedList<>();
    for(String task : taskGraph.vertexSet())
        if(taskGraph.inDegreeOf(task) == 0)
            group.add(task);
    //Next we construct all remaining groups. The group k+1 consists of al vertices without incoming arcs if we were
    //to remove all vertices in the previous group k from the graph.
    do {
        groups.add(group);
        List<String> nextGroup = new LinkedList<>();
        for (String task : group) {
            for (String nextTask : Graphs.neighborSetOf(taskGraph, task)) {
                if (taskGraph.inDegreeOf(nextTask) == 1)
                    nextGroup.add(nextTask);
            }
            taskGraph.removeVertex(task); //Removes a vertex and all its edges from the graph
        }
        group=nextGroup;
    }while(!group.isEmpty());
    return groups;
}
public static Graph<String, DefaultEdge> getGraph1(){
    Graph<String, DefaultEdge> taskGraph=new SimpleDirectedGraph<>(DefaultEdge.class);
    Graphs.addAllVertices(taskGraph, Arrays.asList("A","B","C","D","E"));
    taskGraph.addEdge("A","B");
    taskGraph.addEdge("B","C");
    taskGraph.addEdge("B","D");
    taskGraph.addEdge("C","E");
    taskGraph.addEdge("D","E");
    return taskGraph;
}
public static Graph<String, DefaultEdge> getGraph2(){
    Graph<String, DefaultEdge> taskGraph=new SimpleDirectedGraph<>(DefaultEdge.class);
    Graphs.addAllVertices(taskGraph, Arrays.asList("A","B","C","D","E"));
    taskGraph.addEdge("A","B");
    taskGraph.addEdge("B","C");
    taskGraph.addEdge("B","D");
    taskGraph.addEdge("B","E");
    taskGraph.addEdge("C","E");
    taskGraph.addEdge("D","E");
    return taskGraph;
}
public static void main(String[] args){
    System.out.println("Graph1: "+getGroups(getGraph1()));
    System.out.println("Graph2: "+getGroups(getGraph2()));
}

输出:

Graph1: [[A], [B], [C, D], [E]]
Graph2: [[A], [B], [C, D], [E]]

注意:上面的代码假设输入图确实是一个有效的任务图。您可以构建一个额外的检查来识别循环依赖关系,例如,如果您有这样一个序列: A-

 类似资料:
  • 如何输出有向无环图的所有可能的拓扑排序?例如,给定一个图形,其中 V 指向 W 和 X,W 指向 Y 和 Z,X 指向 Z: 如何对此图进行拓扑排序以产生所有可能的结果?我能够使用广度优先搜索来获得V,W,X,Y,Z,并使用深度优先搜索来获得V,W,Y,Z,X。但无法输出任何其他种类。

  • 是否有一种算法,在给定一个未加权有向无环图的情况下,将所有节点排序到一组节点列表中,从而 保留拓扑顺序(即,对于所有边

  • Wikipedia以一种非常直观的方式(IMO)解释了依赖关系图,引用了当依赖于时,会产生边。换句话说,我们可以通过查看其邻接列表中可用的邻居立即找到任何给定节点的直接依赖关系(如果有的话)。 这似乎是实现依赖的一个明智的方法;它允许我们基本上像执行深度优先遍历(图中每个节点的DFS)一样容易地执行拓扑排序。如果节点表示“任务”,那么只有当任务的所有传递依赖项都已执行/访问时,我们才能执行/访问任

  • 问题内容: 好的,因此在根据输入数据进行拓扑排序时,通常存在多个正确的解决方案,可以根据这些正确的解决方案对图进行“处理”,以便所有依赖项都位于“依赖”它们的节点之前。但是,我正在寻找稍微不同的答案: 假设以下数据: 和(必须先于并且必须先于)。 只有这两个限制,我们有多种候选方案:( ,, 等)。但是,我正在寻找一种将这些节点“分组”的方法,以便在处理完一组后,下一组中的所有条目都将处理其依赖项

  • 问题:给定一个加权有向无环图(DAG)和其中的一个源顶点s,找出从s到给定图中所有其他顶点的最长距离。 请查找参考图:链接 为什么我们需要拓扑排序?我们不能简单地从源顶点使用修改后的BFS吗?为什么我们如此关心线性排序。 如果这是重复,请将我重定向到相关答案。 谢谢

  • 为了表明计算机科学家可以把任何东西变成一个图问题,让我们考虑做一批煎饼的问题。 菜谱真的很简单:1个鸡蛋,1杯煎饼粉,1汤匙油 和 3/4 杯牛奶。 要制作煎饼,你必须加热炉子,将所有的成分混合在一起,勺子搅拌。 当开始冒泡,你把它们翻过来,直到他们底部变金黄色。 在你吃煎饼之前,你会想要加热一些糖浆。 Figure 27将该过程示为图。 Figure 27 制作煎饼的困难是知道先做什么。从 Fi