web-dev-qa-db-ja.com

有向非巡回グラフとは何かを簡単に説明できますか?

有向非巡回グラフとは何かを簡単に説明できますか?私はウィキペディアを調べましたが、プログラミングでの使用を実際に目にすることはありません。

97
Zubair

他のドットを指す線のあるドット

80
smartcaveman

グラフ=エッジで互いに接続されたノードで構成される構造

有向=ノード(エッジ)間の接続には方向があります:A-> BはB-> Aと同じではありません

acyclic = "non-circular" =エッジをたどってノードからノードへ移動すると、同じノードに二度と遭遇することはありません。

有向非巡回グラフの良い例はツリーです。ただし、すべての有向非巡回グラフがツリーであるとは限らないことに注意してください。

160
Roland Bouman

DAG(Directed Acyclic Graph)の意味を示す多くの回答がありますが、そのアプリケーションに関する回答はありません。これは非常にシンプルなものです-

前提条件グラフ-工学コース中、すべての学生は前提条件などの要件に従う科目を選択するタスクに直面します。アルゴリズム[A]の前提条件コースなしでは、人工知能[B]のクラスを受講できないことは明らかです。したがって、BはAに依存するか、より良い意味でAはBに向けられたエッジを持ちます。したがって、Node Bに到達するには、Node Aにアクセスする必要があります。すべてのサブジェクトとその前提条件をグラフに追加すると、有向非巡回グラフになります。

サイクルがあった場合、あなたはコースを完了することはありません:p

学生がコースに登録できるようにする大学のソフトウェアシステムでは、被験者をノードとしてモデル化して、学生が現在のコースに登録する前に前提条件のコースを受講していることを確認できます。

私の教授はこの類推を与え、複雑な概念を使うよりもDAGを理解するのに最も役立ちました!

別のリアルタイムの例-> バージョンシステムでDAGを使用する方法のリアルタイムの例

45
human.js

プログラミングでの有向非巡回グラフの使用例には、接続性と因果関係を表すものが多少なりとも含まれます。

たとえば、実行時に構成可能な計算パイプラインがあるとします。この一例として、A、B、C、D、E、F、Gの計算が互いに依存しているとします。AはCに依存し、CはEとFに依存し、BはDとEに依存し、DはF.これはDAGとして表すことができます。 DAGをメモリに保存したら、次のアルゴリズムを作成できます。

  • 計算が正しい順序で評価されるようにします( topological sort
  • 計算を並列に実行できるが、各計算に最大実行時間がある場合、セット全体の最大実行時間を計算できます

他の多くのものの中で。

アプリケーションプログラミングの領域外では、適切な自動ビルドツール(make、ant、sconsなど)はDAGを使用して、プログラムのコンポーネントの適切なビルド順序を確保します。

24
Jason S

いくつかの回答からグラフの使用例(ネットワークモデリングなど)が得られ、「これはプログラミングとどう関係するのか?」.

そのサブ質問への答えは、プログラミングとはほとんど関係がないということです。問題解決と関係しています。

リンクリストが特定のクラスの問題に使用されるデータ構造であるように、グラフは特定の関係を表すのに役立ちます。リンクされたリスト、ツリー、グラフ、およびその他の抽象的な構造は、コードに実装できるという点で、プログラミングにのみ関連しています。それらは、より高い抽象レベルで存在します。それはプログラミングではなく、問題の解決にデータ構造を適用することです。

13
Jonathon Faust

有向非巡回グラフ(DAG)には、他のグラフと区別する次のプロパティがあります。

  1. エッジは方向を示します。
  2. サイクルはありません。

さて、私は今、1つの使用を考えることができます-DAG( Wait-For-Graphs -more technical details として知られています)プロセスとリソースのセット(両方ともDAGのノード)。サイクルが検出されると、デッドロックが発生します。

これがお役に立てば幸いです。

乾杯

12
Arnkrishn

基本的なグラフの用語はすでにご存知だと思います。それ以外の場合は、 グラフ理論 に関する記事から始める必要があります。

Directedは、エッジ(接続)に方向があるという事実を指します。図では、これらの方向は矢印で示されています。反対は、エッジが方向を指定しない無向グラフです。

Acyclicは、任意のノードXから開始し、可能なすべてのエッジをウォークスルーすると、すでに使用されているものに戻らずにXに戻ることができないことを意味します縁。

いくつかのアプリケーション:

  • スプレッドシート;これは [〜#〜] dag [〜#〜] の記事で説明されています。
  • 改訂管理 :そのページの図を見ると、改訂管理されたコードの進化が指示されていることがわかります(この図では「ダウン」します)および非周期的(それは「アップ」に戻ることはありません)。
  • 家系図:それは指示され(あなたはあなたの両親の子供であり、その逆ではありません)、非周期的です(あなたの祖先は決してあなたの子孫になることはできません)。
11

あらゆる種類のグラフは、プログラミングでさまざまな実世界の関係をモデル化するために使用されます。たとえば、ソーシャルネットワークは多くの場合、グラフ(この場合は循環)で表されます。同様に、ネットワークトポロジ、家系図、航空路線、...

4
tvanfosson

DAGは、すべてが同じ方向に流れ、ノードがそれ自体を参照できないグラフです。

祖先の木について考えてください。それらは実際にはDAGです。

すべてのDAGには

  • ノード(データを保存する場所)
  • 有向エッジ(同じ方向を指す)
  • 先祖ノード(親のないノード)
  • 葉(子を持たないノード)

DAGはツリーとは異なります。ツリーのような構造では、2つのノードごとに一意のパスが必要です。 DAGでは、ノードは2つの親ノードを持つことができます。

DAGに関する良い記事 です。それがお役に立てば幸いです。

3
Mickey

ソースコードまたは3つのアドレス(TAC)コードの観点からでも、このページで問題を簡単に視覚化できます。

http://cgm.cs.mcgill.ca/~hagha/topic30/topic30.html#Exptree

式ツリーセクションに移動し、ページを少し下に移動すると、ツリーの「トポロジカルソート」と、式の評価方法のアルゴリズムが表示されます。

したがって、その場合、DAGを使用して式を評価できます。これは評価が通常解釈されるため便利です。このようなDAGエバリュエーターを使用すると、スタックへのプッシュとポップがなく、また、共通の部分式。

非古代エジプト人(英語)でDAGを計算する基本的なアルゴリズムは次のとおりです。

1)DAGオブジェクトを次のように作成します

ライブリストが必要です。このリストには、現在のすべてのライブDAGノードとDAGサブ式が保持されます。 DAGサブ式はDAGノードですが、内部ノードと呼ぶこともできます。ライブDAG Nodeが意味することは、変数Xに割り当てると、それがライブになるということです。Xを使用する共通の部分式は、そのインスタンスを使用します。新しいDAGノードが作成されてライブリストに追加され、古いXが削除されるため、Xを使用する次のサブ式は新しいインスタンスを参照するため、同じ変数名を使用するだけのサブ式と競合しません。

変数Xに割り当てると、新しい割り当てによって古い値を使用するサブ式の意味が無効になるため、偶然にも、割り当ての時点で有効なすべてのDAG部分式ノードが無効になります。

class Dag {
  TList LiveList;
  DagNode Root;
}

// In your DagNode you need a way to refer to the original things that
// the DAG is computed from. In this case I just assume an integer index
// into the list of variables and also an integer index for the opertor for
// Nodes that refer to operators. Obviously you can create sub-classes for
// different kinds of Dag Nodes.
class DagNode {
  int Variable;
  int Operator;// You can also use a class
  DagNode Left;
  DagNode Right;
  DagNodeList Parents;
}

そのため、たとえばソースコードの式のツリーなど、独自のコードでツリーをウォークスルーします。たとえば、既存のノードXNodesを呼び出します。

そのため、XNodeごとにDAGに追加する方法を決定する必要があり、既にDAGにある可能性があります。

これは非常に単純な擬似コードです。コンパイル用ではありません。

DagNode XNode::GetDagNode(Dag dag) {
  if (XNode.IsAssignment) {
    // The assignment is a special case. A common sub expression is not
    // formed by the assignment since it creates a new value.

    // Evaluate the right hand side like normal
    XNode.RightXNode.GetDagNode();  


    // And now take the variable being assigned to out of the current live list
    dag.RemoveDagNodeForVariable(XNode.VariableBeingAssigned);

    // Also remove all DAG sub expressions using the variable - since the new value
    // makes them redundant
    dag.RemoveDagExpressionsUsingVariable(XNode.VariableBeingAssigned);

    // Then make a new variable in the live list in the dag, so that references to
    // the variable later on will see the new dag node instead.
    dag.AddDagNodeForVariable(XNode.VariableBeingAssigned);

  }
  else if (XNode.IsVariable) {
    // A variable node has no child nodes, so you can just proces it directly
    DagNode n = dag.GetDagNodeForVariable(XNode.Variable));
    if (n) XNode.DagNode = n;
    else {
      XNode.DagNode = dag.CreateDagNodeForVariable(XNode.Variable);
    }
    return XNode.DagNode;
  }
  else if (XNode.IsOperator) {
    DagNode leftDagNode = XNode.LeftXNode.GetDagNode(dag);
    DagNode rightDagNode = XNode.RightXNode.GetDagNode(dag);


    // Here you can observe how supplying the operator id and both operands that it
    // looks in the Dags live list to check if this expression is already there. If
    // it is then it returns it and that is how a common sub-expression is formed.
    // This is called an internal node.
    XNode.DagNode = 
      dag.GetOrCreateDagNodeForOperator(XNode.Operator,leftDagNode,RightDagNode) );

    return XNode.DagNode;
  }
}

それがそれを見る一つの方法です。ツリーの基本的なウォークと、Dagノードを追加して参照するだけです。 DAGのルートは、たとえばツリーのルートが返すDagNodeになります。

当然のことながら、この手順例は小さな部分に分割したり、仮想関数を使用してサブクラスとして作成したりできます。

Dagの並べ替えについては、各DagNodeを左から右に通過します。言い換えると、DagNodesの左側のエッジに続き、右側のエッジに続きます。番号は逆に割り当てられます。言い換えると、子のないDagNodeに到達したら、そのNode=現在のソート番号を割り当て、ソート番号をインクリメントします。これにより、再帰が巻き戻され、番号が昇順に割り当てられます。

この例では、ゼロまたは2つの子を持つノードを持つツリーのみを処理します。明らかにいくつかのツリーには3つ以上の子を持つノードがあるため、ロジックは同じです。左と右を計算する代わりに、左から右に計算するなど...

// Most basic DAG topological ordering example.
void DagNode::OrderDAG(int* counter) {
  if (this->AlreadyCounted) return;

  // Count from left to right
  for x = 0 to this->Children.Count-1
    this->Children[x].OrderDag(counter)

  // And finally number the DAG Node here after all
  // the children have been numbered
  this->DAGOrder = *counter;

  // Increment the counter so the caller gets a higher number
  *counter = *counter + 1;

  // Mark as processed so will count again
  this->AlreadyCounted = TRUE;
}
2
user1401452

この名前は、その定義について知っておく必要があることのほとんどを示しています。すべてのEdgeが一方向にのみ流れるグラフであり、Edgeをクロールすると、パスは直前の頂点に戻りません。

すべての用途について話すことはできません(Wikipediaが役立ちます)が、DAGはリソース間の依存関係を判断する際に非常に役立ちます。たとえば、私のゲームエンジンは、読み込まれたすべてのリソース(マテリアル、テクスチャ、シェーダー、プレーンテキスト、解析されたJSONなど)を単一のDAGとして表します。例:

マテリアルはN GLプログラムで、それぞれに2つのシェーダーが必要であり、各シェーダーにはプレーンテキストシェーダーソースが必要です。これらのリソースをDAGとして表すことで、既存のリソースを簡単にグラフに照会して回避できます複数のマテリアルで同じソースコードの頂点シェーダーを使用したい場合、既存のリソースに新しいEdgeを設定するだけで使用するたびにソースをリロードし、シェーダーを再コンパイルするのは無駄です。また、グラフを使用して、リソースに依存するものがあるかどうかを判断し、依存しない場合は、リソースを削除してメモリを解放します。実際、これはほとんど自動的に行われます。

拡張により、DAGはデータ処理パイプラインの表現に役立ちます。非循環の性質により、同じ頂点に再遭遇することなく、頂点からエッジに沿ってポインターをたどることができるコンテキスト処理コードを安全に記述することができます。 [〜#〜] vvvv [〜#〜]Max MSP 、またはAutodesk Mayaのノードベースのインターフェイスなどのビジュアルプログラミング言語はすべてDAGに依存しています。

1

プログラミングに含まれるツリーを知っている場合、プログラミングのDAGは似ていますが、ノードに複数の親を持つことができます。これは、ノードを1つ以上の親の下に集中させたいが、サイクルを持つ一般的なグラフの結び目の混乱の問題がない場合に便利です。それでもDAGを簡単にナビゲートできますが、ルートに戻る方法は複数あります(複数の親が存在する可能性があるため)。一般に、単一のDAGには複数のルートを含めることができますが、実際には、ツリーのように1つのルートに固執することをお勧めします。 OOPで単一の継承と複数の継承を理解しているなら、ツリーとDAGを知っています。私はすでにこれに答えました こちら

1
Jamin