繁体   English   中英

给定一个非循环有向图,返回一组“在同一级别”的节点集合?

[英]Given an acyclic directed graph, return a collection of collections of nodes “at the same level”?

首先,我不确定这样的算法是什么,这是主要的问题 - 所以问题的第一部分是这个算法叫做什么?

基本上我有一个DiGraph() ,我插入节点[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]和边缘([1,3],[2,3],[3,5],[4,5],[5,7],[6,7],[7,8],[7,9],[7,10])

从这里我想知道是否有可能获得如下收集: [[1, 2, 4, 6], [3], [5], [7], [8, 9, 10]]

编辑:如果它有帮助,让我添加一些约束。 - 没有循环,这是有保证的 - 图表没有一个起点

我正在尝试做的是收集同一级别的节点,以便它们的处理可以并行化,但在外部集合中,处理是串行的。

EDIT2:很明显我没有想过这个,所以最简单的描述“级别”的方法是最深层的前任 ,所有节点都具有相同深度的前辈。 因此,上面列表中的第一个条目是所有节点,其中0是最前面的,第二个有一个,第三个有两个,依此类推。 在每个列表中, 兄弟姐妹的顺序是无关紧要的,因为它们将被并行处理。

您的问题表明您希望此图表的输出为[[1, 2, 4, 6], [3], [5], [7], [8, 9, 10]] IIUC,模式如下:

  • [1, 2, 4, 6]是没有边缘的节点。

  • 假设所有先前的节点都被擦除, [3]是没有边缘的节点。

  • 假设所有先前的节点都被擦除, [4]是没有边缘的节点。

  • 等(直到所有节点都被删除)

假设我们开始

g = networkx.DiGraph()
g.add_edges_from([[1,3],[2,3],[3,5],[4,5],[5,7],[6,7],[7,8],[7,9],[7,10]])

然后我们可以将其编码为

def find_levels(g):
    levels = []
    while g.nodes():
        no_in_nodes = [n for (n, d) in g.in_degree(g.nodes()).items() if d == 0]
        levels.append(no_in_nodes)
        for n in no_in_nodes:
            g.remove_node(n)
    return levels

如果我们运行它,我们得到结果:

>>> find_levels(g)
[[1, 2, 4, 6], [3], [5], [7], [8, 9, 10]]

这里的复杂度是Θ(| V | 2 + | E |) 可以使用Fibonnacci Heap构建更复杂的版本。 基本上,所有顶点都需要放入堆中,每个级别由度数为0的顶点组成。 每次弹出一个,并删除其他顶点的边缘,我们可以将其转换为堆减少键操作(剩余顶点的度数减少)。 这会将运行时间减少到Θ(| V | log(| V |)+ | E |)

Ami表示,拓扑排序将实现这一目标。 以下是Boost Graph Library实现,没有上下文,但可以提取伪代码。 toporder对象只提供拓扑排序的迭代器。 如果需要,我可以提取通用算法。

template<typename F>
void 
scheduler<F>::set_run_levels()
{

  run_levels = std::vector<int>(tasks.size(), 0);
  Vertexcont toporder;

  try
    {
      topological_sort(g, std::front_inserter(toporder));
    }
  catch(std::exception &e)
    {
      std::cerr << e.what() << "\n";
      std::cerr << "You most likely have a cycle...\n";
      exit(1);
    }

  vContIt i = toporder.begin();

  for(;
      i != toporder.end();
      ++i)
    {
      if (in_degree(*i,g) > 0)
        {
          inIt j, j_end;
          int maxdist = 0;
          for(boost::tie(j,j_end) = in_edges(*i,g);
              j != j_end;
              ++j)
            {
              maxdist = (std::max)(run_levels[source(*j,g)], maxdist);
              run_levels[*i] = maxdist+1;
            }
        }
    }
}

我想我曾经把它应用到同样的问题,然后意识到这是不必要的。 只需在头发触发器上设置任务,所有任务都向其家属发出信号(通过condition_variable,promise)。 所以我需要的只是知道每个任务的依赖关系,找到初始任务,然后开火。 您的案例中是否需要完整的run_level规范?

为什么bfs无法解决呢? bfs算法是广度遍历算法,即它遍历树级。 这也意味着,同一个级别的所有节点都会被遍历,这是您想要的输出。 正如评论中指出的那样,这将假设图中的起点。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM