簡體   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