誰かが簡単な言葉で有向非巡回グラフとは何かを説明できますか?


109

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


26
ウィキペディアには、初心者が理解するために多くの勉強を必要とする圧倒的な技術コンテンツが頻繁に含まれています。数学のヘルプサイトの多くはこの点で優れていますが、残念ながら、計算に関連する話題には入らない傾向があります。
Jonathon Faust

1
gitを使用する人は誰でも、知らないうちにDAGを実際に使用します。ericsink.com
vcbe / html /

回答:


86

他の点を指す線のある点


23
これは、複雑な用語に埋もれている単純な概念を簡単に説明する方法であるため、これは最良の回答の1つです(この質問をしている場合、グラフ理論がわからない、または知る必要があるかもしれません)。私のバリエーションは、「同じバーに2度行くことができないバーホッピング」のようなものです。別の回答の家系図の例は、おそらく大学生やアルコール依存症ではない私たちの場合は特に、おそらく概念的に単純です。
トムハリソン

27
...一方向
マークロブソン2017

3
これは、本質的に複雑な概念を可能な用語よりも少なく表現できないことの良い例です。これが、ユークリッドの5番目の仮説が依然として存在する理由です。
Xaqron

4
「線がサイクルを形成しない場所」を含める必要があります。そうでない場合は、有向非循環グラフではなく、有向グラフを記述するだけです。
Pharap

「線の付いたドットは他のドットを指し、ループはありません」と改善されます。
John DeRegnaucourt

172

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

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

acyclic = "non-circular" =エッジに沿ってノードからノードに移動すると、同じノードに2回目に遭遇することはありません。

有向非循環グラフの良い例はツリーです。ただし、すべての有向非循環グラフがツリーであるとは限りません。


ノードとは何ですか。「エッジ」とは、ノードAからノードBを指す矢印ですか。
appshare.co 2010

より良い説明。では、これはプログラミングとどう関係しているのでしょうか?関数型プログラミングに関連していますか?
appshare.co 2010

2
通常は矢印で表されますが、実際にはAとBの間に関係があるだけです。プログラムでは、これは隣接行列の2つのノードを表すインデックスの真の値である可能性があります。
tvanfosson 2010

42
すべての有向ツリーはDAGですが、すべてのDAGがツリーであるとは限りません。ノードCには複数の親があるため、DAG A-> B、A-> C、B-> Cはツリーとして表すことができません。
Jason S

2
エッジの方向性は、DAGをツリーから分離する唯一の機能ではありません。DAGは、ツリーとは異なり、| V | -1以上のエッジを持つことができます。たとえば、A-> B、A-> C、B-> D、C-> DはDAGですが、エッジとノードの数が同じであるため、ツリーではありません。
匿名Mus

49

DAG(有向非巡回グラフ)の意味を示す多くの回答が表示されますが、そのアプリケーションについては回答がありません。これは非常にシンプルなものです-

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

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

学生がコースに登録できる大学のソフトウェアシステムは、学生が現在のコースに登録する前に必須のコースを受講していることを確認するために、ノードとして科目をモデル化できます。

私の教授はこのアナロジーを与えました、そしてそれは私がいくつかの複雑な概念を使うよりもDAGを理解するのに最も役立ちました!

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


4
これが最も高いランクの答えになるはずです。単純な類推で、OPが簡単に理解できないテキストブックの定義を使用していません。
kimathie 2017

25

プログラミングにおける有向非循環グラフの使用例には、接続性と因果関係を表す多かれ少なかれ何でも含まれます。

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

  • 計算が正しい順序で評価されることを確認します(トポロジカルソート
  • 計算を並行して実行できるが、各計算に最大実行時間がある場合は、セット全体の最大実行時間を計算できます

他の多くのものの間で。

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


因果関係について言及する場合は+1。これは、1つのプロセスの出力が1つ以上の他のプロセスの入力である複雑なシステムを表す必要がある場合に多く発生します。
Alex Feinman、2010

14

いくつかの回答がグラフの使用例(ネットワークモデリングなど)を示しており、「これはプログラミングとどのような関係があるのか​​」と尋ねました。

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

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


プログラミングで実装できます。はい、グラフはコンピュータに依存しない現実世界に存在するので、私はそれが好きです!
appshare.co 2010

13

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

  1. それらのエッジは方向を示します。
  2. 彼らはサイクルを持っていません。

さて、私は今のところ1つの使用法を考えることができます-DAG(Wait-For-Graphsとして知られている -より技術的な詳細)は、プロセスとリソースのセット(どちらもDAGのノードです)間の依存関係を示しているため、デッドロックを検出するのに便利です。サイクルが検出されるとデッドロックが発生します。


1
Andriyev、デッドロックの例では+1。これは実際にはMySQLのInnoDBエンジンで使用されており、「その行のロックが解放されるのをその行が待たなければならない」のように「グラフ待ち」と呼ばれます
Roland Bouman

はい、あなたは「グラフを待つ」という名前で正解です。一部はそれを逃した。応答を更新しました。:)
Arnkrishn

依存関係があることをどのようにして知るのですか?2つのノードに共通の祖先があるかどうかを確認することによってですか?
appshare.co 2010

このリンク-cis.temple.edu/~ingargio/cis307/readings/deadlock.htmlには、より技術的な詳細があります。
Arnkrishn、2010

11

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

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

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

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

  • スプレッドシート; これはDAGの記事で説明されています。
  • リビジョン管理:そのページの図を見ると、リビジョン管理されたコードの進化が方向付けられており(この図では「下」になり)、非循環的である(「上に戻る」ことはない)ことがわかります。
  • 家系図:それは方向付けられており(あなたはあなたの両親の子供であり、逆ではありません)、非周期的です(あなたの祖先はあなたの子孫になることはできません)。

5

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

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

すべてのDAGには

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

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

DAGに関する優れた記事を次に示します。お役に立てば幸いです。


4

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


2

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

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

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

したがって、その場合、DAGを使用して式を評価できます。これは、評価が通常解釈され、そのようなDAGエバリュエーターを使用すると、スタックへのプッシュとポップが行われないため、また、排除されるため、基本的にシンプルなインタープリターが高速になります。一般的な部分式。

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

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

ライブリストが必要です。このリストには、現在のすべてのライブDAGノードとDAGサブ式が保持されます。DAGサブ式はDAGノードですが、内部ノードと呼ぶこともできます。ライブDAGノードとは、変数Xに割り当てるとライブになるということです。Xを使用する一般的な部分式は、そのインスタンスを使用します。Xが再度割り当てられると、NEW DAG NODEが作成されてライブリストに追加され、古い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を左から右に移動します。言い換えると、DagNodeの左側の端に続いて、右側の端に続きます。番号は逆に割り当てられます。つまり、子のないDagNodeに到達したら、そのノードに現在の並べ替え番号を割り当て、並べ替え番号をインクリメントします。これにより、再帰が解かれ、番号が昇順に割り当てられます。

この例では、子が0個または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;
}

1

プログラミングのツリーがわかっている場合、プログラミングのDAGは似ていますが、ノードに複数の親を持たせることができます。これは、ノードを単一の親だけでなく複数のノードの下にまとめたいが、循環する一般的なグラフの結びついた混乱の問題がない場合に便利です。DAGは簡単にナビゲートできますが、ルートに戻るには複数の方法があります(複数の親が存在する可能性があるため)。単一のDAGは一般に複数のルートを持つことができますが、実際には、ツリーのように1つのルートに固執する方が良い場合があります。OOPの単一継承と複数継承を理解していれば、ツリーとDAGがわかります。私はすでにここでこれに答えまし


1

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

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

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

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


-5

有向非巡回グラフは、...非有向グラフを表現したい場合に便利です!正規の例は家系図または系図です。


ああ、それも理にかなっています。しかし、それでも、これはプログラミングとどう関係しているのでしょうか。
appshare.co 2010

1
データ構造はプログラミングとどのような関係がありますか?
ジョナサンファインバーグ

わかりました。それはあなたがあなたの答えで「データ構造」に言及しなかったことだけです
appshare.co

5
トートロジー!=説明
エヴァ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.