有人能用简单的语言向我解释一个有向无环图是什么?

时间:2010-02-17 19:26:44

标签: directed-acyclic-graphs

有人可以用简单的语言向我解释一下有向无环图是什么吗?我看过维基百科,但它并没有真正让我看到它在编程中的用途。

13 个答案:

答案 0 :(得分:157)

graph =由节点组成的结构,它们通过边连接到彼此

directed =节点(边缘)之间的连接具有方向:A - > B与B - >不相同;一个

acyclic =“non-circular”=通过跟随边缘从节点移动到节点,第二次永远不会遇到相同的节点。

有向无环图的一个很好的例子是树。但请注意,并非所有有向无环图都是树。

答案 1 :(得分:73)

带有指向其他点的线的点

答案 2 :(得分:44)

我看到许多答案表明DAG(有向无环图)的含义,但没有答案对其应用程序。这是一个非常简单的 -

先决条件图 - 在工程课程中,每个学生都面临着选择遵循诸如先决条件等要求的科目的任务。现在很明显,如果没有算法[A]的必修课程,就不能参加人工智能课程[B]。因此B依赖于A或者更好地表示A具有指向B的边缘。因此,为了到达节点B,您必须访问节点A.很快就会清楚地将所有具有其先决条件的主题添加到图形中,它将变成一个有向无环图。

  

如果有一个循环,那么你永远不会完成一个课程:p

大学中允许学生注册课程的软件系统可以将科目建模为节点,以确保学生在注册当前课程之前已经参加了必修课程。

我的教授给出了这个类比,它最好帮助我理解DAG,而不是使用一些复杂的概念!

另一个实时示例 - > Real Time example of how DAG's can be used in version system

答案 3 :(得分:23)

在编程中使用有向无环图包含或多或少的任何代表连通性和因果关系的东西。

例如,假设您有一个可在运行时配置的计算管道。作为一个例子,假设计算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来确保程序组件的正确构建顺序。

答案 4 :(得分:13)

有几个答案给出了使用图表的例子(例如网络建模),你已经问过“这与编程有什么关系?”。

该子问题的答案是它与编程没有多大关系。它与解决问题有关。

就像链表是用于某些类问题的数据结构一样,图表对于表示某些关系很有用。链接列表,树,图和其他抽象结构只与编程有关,因为您可以在代码中实现它们。它们存在于更高的抽象层次。它不是关于编程,而是关于在问题解决方案中应用数据结构。

答案 5 :(得分:12)

定向非循环图(DAG)具有以下属性,可将它们与其他图区分开来:

  1. 他们的边缘显示方向。
  2. 他们没有周期。
  3. 好吧,我现在可以想到一个用法--DAG(称为Wait-For-Graphs - 更多technical details)在检测死锁时非常方便,因为它们说明了一组进程和资源之间的依赖关系(两者都有)是DAG中的节点)。检测到循环时会发生死锁。

    我希望这会有所帮助。

    欢呼声

答案 6 :(得分:11)

我假设你已经知道了基本的图形术语;否则你应该从graph theory上的文章开始。

定向指的是边(连接)有方向的事实。在图中,这些方向用箭头表示。相反的是无向图,其边缘不指定方向。

非循环意味着,如果您从任意节点X开始并遍历所有可能的边缘,则无法返回到X而不返回已使用的边缘。

几个应用程序:

  • 电子表格;这在DAG文章中有解释。
  • Revision control:如果您查看该页面中的图表,您会看到修订控制代码的演变是针对的(它在图中“向下”)和非循环(从不回去“up”)。
  • 家谱:它是针对的(你是你父母的孩子,而不是相反)和非循环(你的祖先永远不可能是你的后代)。

答案 7 :(得分:4)

所有种类的图形用于编程以模拟各种不同的现实世界关系。例如,社交网络通常由图表表示(在这种情况下为循环)。同样,网络拓扑,家谱,航线,...

答案 8 :(得分:3)

DAG是一个图表,其中所有内容都以相同的方向流动,没有节点可以引用回自身。

想想祖先的树木;他们实际上是DAG。

所有DAG都有

  • 节点(存储数据的地方)
  • 定向边(指向同一方向)
  • 祖先节点(没有父节点的节点)
  • 离开(没有孩子的节点)

DAG与树木不同。在树状结构中,每两个节点之间必须有一条唯一的路径。在DAG中,节点可以有两个父节点。

这里是good article about DAGs。我希望有所帮助。

答案 9 :(得分:2)

从源代码甚至三个地址(TAC)代码的角度来看,您可以在此页面上轻松地直观地显示问题......

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

如果你转到表达式树部分,然后向下翻页,它会显示树的“拓扑排序”,以及如何评估表达式的算法。

因此,在这种情况下,您可以使用DAG来计算表达式,这很方便,因为通常会解释评估,并且使用这样的DAG求值程序会使原始的int表示更快,因为它不会推送和弹出到堆栈,也因为它正在消除常见的子表达式。

在非古埃及(即英语)中计算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;
}

因此,您所做的就是在您自己的代码中遍历您的树,例如源代码中的表达式树。例如,调用现有节点XNode。

因此,对于每个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时,为该节点分配当前的排序编号并增加排序编号,以便递归展开时按升序顺序分配数字。

此示例仅处理具有零个或两个子节点的节点的树。显然,有些树的节点有两个以上的子节点,因此逻辑仍然相同。而不是左右计算,从左到右等计算......

// 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;
}

答案 10 :(得分:1)

如果您知道编程中的树是什么,那么编程中的DAG类似,但它们允许节点具有多个父节点。当你想让一个节点不仅仅是单个父节点聚集在一起时,这可能很方便,但是没有带有循环的常规图形打结的问题。您仍然可以轻松导航DAG,但有多种方法可以返回到根目录(因为可以有多个父级)。单个DAG通常可以有多个根,但实际上可能更好地坚持一个根,就像一棵树。如果您了解OOP中的单个与多个继承,那么您就知道树与DAG。我已经回答了这个here

答案 11 :(得分:1)

这个名字告诉你大部分你需要知道它的定义:它是一个图形,其中每个边缘只在一个方向上流动,一旦你向下爬行,你的路径永远不会让你回到你刚才的顶点剩下。

我不能谈论所有用途(维基百科帮助那里),但对我来说,DAG在确定资源之间的依赖关系时非常有用。我的游戏引擎例如将所有加载的资源(材料,纹理,着色器,明文,解析的json等)表示为单个DAG。例如:

材质是N GL程序,每个都需要两个着色器,每个着色器需要一个明文着色器源。通过将这些资源表示为DAG,我可以轻松地查询图表以查找现有资源,以避免重复加载。假设您希望多种材质使用具有相同源代码的顶点着色器。当您可以为现有资源建立新边缘时,重新加载源并为每次使用重新编译着色器都是浪费。通过这种方式,您还可以使用图形来确定是否有任何内容依赖于资源,如果没有,则删除它并释放其内存,实际上这几乎是自动发生的。

通过扩展,DAG可用于表达数据处理流水线。非循环性意味着您可以安全地编写上下文处理代码,该代码可以跟随指向顶点边缘的指针而无需重新计算相同的顶点。像VVVVMax MSP或Autodesk Maya的基于节点的接口等可视化编程语言都依赖于DAG。

答案 12 :(得分:-4)

当您想要表示...有向无环图时,有向无环图很有用!典型的例子是家谱或家谱。