【问题标题】:A human-readable textual representation of a Directed Acycling Graph有向循环图的人类可读文本表示
【发布时间】:2020-01-15 12:21:14
【问题描述】:

一棵树有一堆人类和机器可读的文本表示形式——例如嵌套列表(以各种表示形式——例如 JSON 和 YAML)和 XML。结合缩进,它们可以很容易地想象得到的结构。

但我没有看到任何与Directed Acyclic Graph 具有相同可读性的东西。它是一种比树更通用的数据结构,因此不能使用上述格式(无论如何,逐字)。

  • 我见过的所有人类可读的表示都是图形化的
  • 原始文本表示将列出所有节点及其连接 - 如果节点数量超过几个,则很难想象图表

我想到的应用程序将是各种流程图——例如自然而然地出现在各种计划任务中。


为了限制问题的范围,我主要要求提供标准解决方案,或者至少是生产就绪并证明在某些实践领域有效。如果没有,任何通过某种同行评审(例如在已发表的科学论文中提出)的实验命题都必须这样做。

【问题讨论】:

  • 我看到的唯一答案是你排除的那个,The raw textual representation would be to list all nodes and their connections
  • 我喜欢的唯一一个可以工作并且可以转换为文本的图表是可见性表示。请参阅:Planar Orthogonal and Polyline Drawing Algorithms 部分:7.2.3 可见性表示
  • 现在有几个答案,您应该创建一个adjacency list,说明您希望看到的每个答案的完成情况,以便您将苹果与苹果进行比较。
  • 同意@GuyCoder,一些测试用例比较好。
  • 我猜“测试用例”是指一些示例 DAG,因此每个答案都可以以苹果对苹果的方式显示表示形式。

标签: language-agnostic directed-acyclic-graphs representation


【解决方案1】:

这是可见性表示的一个简单变体。请参阅:Planar Orthogonal and Polyline Drawing Algorithms 部分:7.2.3 可见性表示

A -> B
A -> C
A -> E
B -> D
B -> F
C -> D
C -> G
D -> H
E -> F
E -> G
E -> G
F -> H
 G--------G--------G                
 |        |        |        
 |     H--H--H     |    
 |     |     |     |
 | F---F     D---D |     
 | |   |     |   | |   
 E-E   B--B--B   C-C  
   |      |      |       
   A------A------A

这里是this

A -> B
A -> C
A -> D
B -> E
B -> F
C -> E
C -> G
D -> F
D -> G
E -> H
F -> H
G -> H

)|( 代表一个网桥,没有连接。

          H--H--H          
          |  |  |           
  E-------E  |  G----G--G   
  |       |  |  |       |   
  |  F---)|(-F-)|(---F  |   
  |  |    |     |    |  |   
  B--B    C--C--C    D--D   
  |          |          |   
  A----------A----------A 

来自:Orthogonal Graph Drawing with Constraints 图3.4(a)

a -> b
a -> c
a -> d
b -> c
b -> e
c -> f
d -> e
d -> f
d -> g
e -> f
e -> h
f -> i
g -> h
g -> i
h -> i
         i---i-------i   
         |   |       |       
 f---f---f   h---h   |   
 |   |   |   |   |   |   
 |   |   e---e   g---g  
 |   |   |   |   |       
 |   d---d--)|(--d       
 |           |   |        
 c---c       |   |        
 |   |       |   |        
 |   b-------b   |        
 |   |           |        
 a---a-----------a    

【讨论】:

  • 作为一种可视化格式很好,但实际使用起来会很棘手。与 Git 的选择一样,我想说:以更复杂的布局和更冗长为代价的更清晰、更直接的绘图和解析。
  • 我真的很喜欢这些图表的外观。就顶点和弧的数量而言,这些生成有多难?多项式时间?多对数?空间站?
【解决方案2】:

我将使用 带有锚点的 YAML 嵌套列表。 (这相当于带有实体的 XML,但后者有更多噪音。)
(我已经在考虑它,但想知道是否发明了更好的东西。看起来还没有。但最重要的是,@ Patrick87 正式表明这是一个足够的表示。)

如果我将组合替换为缩进,而联合替换不缩进,则相当于formal regular expression representation suggested by @Patrick87;当被多次引用时,锚点可以消除节点下子图的重复。


例如@GuyCoder 的例子

A->B
A->C
A->D
B->E
B->F
C->E
C->G
D->F
D->G
E->H
F->H
G->H

对应于A(B(E+F)+C(E+G)+D(F+G))HA(B(EH+FH)+C(EH+GH)+D(FH+GH))

应该是

- A
    - B
        - &E E
            - &H H
        - &F F
            - *H
    - C
        - *E
        - &G G
            - *H
    - D
        - *F
        - *G

(为了统一,每个原始节点都可以作为锚点,例如生成它。)


图表是否是平面的并不重要,因为任何横切链接都不是“绘制”的。

作为奖励,它允许将附加到每个节点的数据指定为以该节点为根的哈希表。 (虽然超过了一定的大小,但单独放置数据可能会更清楚。)

【讨论】:

    【解决方案3】:

    在形式语言和自动机理论中,最重要的结果之一是确定性有限自动机和(形式)正则表达式之间的等价性。 DFA 只是带有一些额外信息的标记有向图。我提出以下建议:(1)考虑 DAG 中的所有状态都接受(2)用弧开始的顶点标签标记每个弧(3)为此 DFA 生成正则表达式(选择拓扑最小状态为不同 DFA 的起始状态)。首先,您需要对顶点进行拓扑排序,然后为每个连接的组件创建一个 DFA/正则表达式对。

    示例:节点 A 去节点 B 和 C,B 去 D 和 E,C 去 E 和 F。

    拓扑排序发现已经按标签字母顺序排序的顶点:A,B,C,D,E。从拓扑最小节点A开始有一个连通分量。

    在标记弧之后遍历算法可能会给你一个正则表达式,如 A(B(D+E)+C(E+F))。

    请注意,由于图是非循环的,因此您永远不需要 Kleene 闭包符号星号/星号。如果有兴趣,可以详细说明这个答案。

    编辑:一些详细说明。

    在cmets中指出,上面的正则表达式有一些重复。这是真实的。然而,这可能不会变成灾难性的重复:我们可以避免重复子图,至少在某种程度上是这样。例如,假设在上面的示例中有一个具有拓扑最小节点 E 的长子图。假设它具有可接受的正则表达式 r。那么我们可以将上面的正则表达式调整为:A((B+C)Er + BD + CF)。仍然存在重复,现在 B 和 C 之间而不是 E 之间,但是由于具有拓扑最小节点 E 的子图,这仍然是一个更简洁的表示。

    最小化通用正则表达式是 PSPACE 完备的。但是,我不知道这个界限是否适用于最小化从 DFA 生成的正则表达式,其图形是 DAG。正如 cmets 正确指出的那样,DFA 和 RE 的常规理论处理比 DAG 更复杂的一般有向图。完全有可能没有 Kleene 星的正则表达式可能比有它的更容易最小化。这可能是一个值得单独提出的问题,可以在 cs.stackexchange.com 上提出。

    另一种表示 DFA 的常用方法是使用常规语法。这本质上等同于在 DFA 的状态图中简单地列出对应于弧的有序节点对。

    EDIT2:一个例子

    另一个答案有这个例子:

    A->B
    A->C
    A->D
    B->E
    B->F
    C->E
    C->G
    D->F
    D->G
    E->H
    F->H
    G->H
    

    我怀疑此处描述的几乎最小的正则表达式将大致如下:A(B(E+F)+C(E+G)+D(F+G))H。我们的表示比较如下:

    • 24 个语法等效符号,11 个正则表达式
    • 语法等效的运算符有 12 个,正则表达式的运算符有 8 个
    • 总共 36 对 19 个令牌

    很难与可见性图进行比较,因为表示方式如此不同,但正则表达式显然使用较少的总符号(如果我们计算符号的话)。

    编辑3:提出了另一个例子,如下:

    a -> b
    a -> c
    a -> d
    b -> c
    b -> e
    c -> f
    d -> e
    d -> f
    d -> g
    e -> f
    e -> h
    f -> i
    g -> h
    g -> i
    h -> i
    

    我可以用手想出的最简洁的正则表达式(第二个猜测,没有方法)是:a((b+d)e(f+h)+(bc+c+d)f+dg( h+#))i 。请注意,# 不是通常的正式正则表达式语法的一部分,它表示空正则表达式,也就是说,它生成由空字符串组成的语言。这是一种方便,可以更好地进行最小化,并且不增加计算能力,只增加表现力。如果你不喜欢它,你可以使用 dgh + dh 来代替,它只是长了一个符号。这种表示仍然是原始语法大小的一半左右。实际上,语法和可见性图的比较是相似的 w.r.t。最后一个例子的那些。

    编辑 4:我现在将扩展上一个示例中的表达式,以显示它是通过 DAG 的不相交路径联合的因式表示。

    a((b+d)e(f+h)+(bc+c+d)f+dg(h+#))i
    a((b+d)e(f+h)+(bc+c+d)f+dgh+dg)i
    a((b+d)ef+(b+d)eh+(bc+c+d)f+dgh+dg)i
    a(bef+def+beh+deh+(bc+c+d)f+dgh+dg)i
    a(bef+def+beh+deh+bcf+cf+df+dgh+dg)i
    abefi+adefi+abehi+adehi+abcfi+acfi+adfi+adghi+adgi
    

    【讨论】:

    • (另请注意,表示 DFA 的另一种规范方法是使用常规语法,这基本上是 slebetman 的答案所建议的。语法列出了所有产生式,基本上对应于列出由弧。)
    • @ivan_pozdeev 我已经更新了答案,请看看您是否觉得这有所改善。重读您的问题后,我注意到您提到该应用程序是流程图。鉴于此,这个答案可能比我意识到的更像你所追求的。 DFA 正是流程图,围绕它们有丰富的理论,并将它们转换为正则表达式、正则语法,然后再转换回来。您的图表是非循环的,这可能会对您有所帮助。它可能会使所有问题变得比一般(循环)自动机理论中的问题更容易。
    • @GuyCoder 最后的 H 编码了这样一个事实,即通过 DAG 的所有路径都以 H 结束。您可以使用诸如乘法和加法(此处为连接和联合)之类的分配律来扩展此合并的表达式转换为从 A 到 H 的所有简单路径的联合。但通常我同意正则表达式不一定比语法“更清晰”,但它们可以更简洁(有时它们提供语法可能没有的见解)。当然,反之亦然。
    • @GuyCoder 我做了代数,根据您所做的图表,结果看起来正确。我的原始表达式缺少我添加的 f(我当时考虑错误或只是忘记输入该部分)。最后一行中的术语正是通过您的图表的路径。术语中的相邻符号对与语法的产生完全对应。
    • @GuyCoder 对,这对路径而不是连接进行编码。如果 DAG 是 DFA,则这些项将是自动机接受的字符串,而 RE 将匹配这些字符串。如果您愿意,可以使用已知算法从 RE 中恢复 DFA(和语法,即连接列表),并且对于这个 RE 子集来说,这样做可能比一般情况下容易得多。根据应用程序,您可能更关心整个路径而不是单个连接(例如,模式匹配)。这对 OP 是否有用,只有 OP 可以说。
    【解决方案4】:

    图形的一个很好的文本表示是来自graphvizdot 语言。语法是易于阅读的关系描述。

    请注意,graphviz 背后的核心思想不是从图表开始并描述它,而是不知道图表是什么样子,从你知道的开始,然后让graphviz 为你生成图表。由于这个设计目标graphviz 没有任何手动放置节点的功能 - 它会根据您选择的算法自动绘制节点。

    以下是使用 graphviz 的示例:

    假设您要绘制公司的组织结构图。您还不知道图表是什么样子,但您知道谁向谁报告。您可以这样描述公司:

    digraph {
        CEO                ->  Board_of_Directors
        CTO                ->  CEO
        CFO                ->  CEO
        COO                ->  CEO
        DevLead            ->  CTO
        DevTeam            ->  DevLead
        DevOps             ->  CTO
        Head_of_Accounting ->  CFO
        Accountants        ->  Head_of_Accounting
        Procurement        ->  Head_of_Accounting
        Procurement        ->  COO
        Logistics          ->  COO
        Tech_support       ->  CTO
        Tech_support       ->  COO
    }
    

    使用dot 算法运行此程序将生成以下图表:

    Graphviz 确实具有复杂的节点和边描述功能,例如定义节点的形状、边的标签等。但是添加这些细节通常会降低图形的可读性,因为现在大部分代码看起来像样式定义节点之间的关系。不过,我认为图形定义本身相当干净。与任何语言一样,它旨在用于解决人类问题(在这种情况下,如果您知道状态和转换,则可以弄清楚图形的外观)并且必须至少在一定程度上可用于其预期用途。

    【讨论】:

    • 我经常使用的一个技巧是让 graphviz 在 SVG 中生成图形,然后将生成的图形导入 Inkscape 或 Adob​​e Illustrator 等矢量编辑器
    • 这是节点对的列表,因此 IMO 没有可读性奖励。由于简洁的语法和内置的可视化,仍然是一个可行的选择,所以感谢您的回答!
    • 你能展示一下 Guy Coder 的例子是什么样的吗?我很好奇它如何处理非平面 DAG。它对这些情况的处理是否可配置?
    • @Patrick87 就像他的回答一样。他的回答包括与上面x -> y 完全相同的语法。至于绘图,线条会简单地交叉
    • @Patrick87 示例图 - google.com/…
    【解决方案5】:

    git log --graph 以文本方式表示提交的有向图。 (在 Git 中,多个提交可以从一个提交中分支出来,两个提交可以合并为一个新提交,从旧提交到新提交的时间可能是一个方向。所以它是一个没有循环的有向图)

    --format...%p 包括父提交,因此它可能被认为是机器可读的。例如

    git log --graph --abbrev-commit --decorate --format=format:'%h - %aD %p %s'
    

    会显示类似:

    *   585a502 - Mon, 23 Jul 2012 19:13:28 +0100 1ce012a 00b3327 Merge github.com:orangeduck/CPlus
    |\
    | *   00b3327 - Mon, 23 Jul 2012 07:38:16 -0700 9a24ec6 5c5b6e2 Merge pull request #4 from felipecruz/feature/create_tests_dir
    | |\
    | | * 5c5b6e2 - Mon, 23 Jul 2012 10:04:07 -0300 11fb96d move tests to tests dir
    | | * 11fb96d - Sun, 22 Jul 2012 18:19:51 -0300 9a24ec6 fix variable name in Strind_Discard
    | |/
    * | 1ce012a - Mon, 23 Jul 2012 19:13:05 +0100 9a24ec6 Array instances Push. Used memmove for dynamic container manipulation.
    |/
    * 9a24ec6 - Sun, 22 Jul 2012 14:02:24 +0100 b41f9c2 Better documented source
    * b41f9c2 - Sun, 22 Jul 2012 12:50:13 +0100 2f4b862 Assign class. Added WIP Array Type.
    

    * 的位置 - 伪图形中的节点、行和后续提交 - 方向从较早(底部)到较新(顶部)的边

    更新

    这里的非平面 DAG 看起来要笨拙得多。非平面的示例,与维基百科中的this 示例相同:

    *   094f405 - Thu, 19 Sep 2019 03:51:50 +0300 ab942ca 76ee1ad X, Y, Z
    |\
    | *   76ee1ad - Thu, 19 Sep 2019 03:49:49 +0300 b1ea78b 8d32234 Y, Z
    | |\
    * | \   ab942ca - Thu, 19 Sep 2019 03:51:17 +0300 b20cf0b 7f714d9 XY, XZ
    |\ \ \
    | * \ \   7f714d9 - Thu, 19 Sep 2019 03:49:05 +0300 b84010c 8d32234 X, Z
    | |\ \ \
    | | | |/
    | | |/|
    | | * | 8d32234 - Thu, 19 Sep 2019 03:45:18 +0300 6927afa Z
    * | | |   b20cf0b - Thu, 19 Sep 2019 03:47:04 +0300 b84010c b1ea78b X, Y
    |\ \ \ \
    | |/ / /
    |/| | /
    | | |/
    | |/|
    | * | b1ea78b - Thu, 19 Sep 2019 03:44:40 +0300 6927afa Y
    | |/
    * | b84010c - Thu, 19 Sep 2019 03:43:53 +0300 6927afa X
    |/
    * 6927afa - Thu, 19 Sep 2019 03:31:01 +0300 4bf9923 #4
    

    还有许多降价支持有向图,例如this one(谷歌搜索的第一个)

    【讨论】:

    • 如何显示非相邻文件之间的链接?例如。从 11fb96d 直接合并到 master,反之亦然?
    • @ivan_pozdeev,我添加了一张有向无环图的图片,并将父提交包含在文本输出中,因此文本现在有足够的信息来构建图。有不相邻的节点,例如1ce012a 和 00b3327 合并为 585a502。我不确定反之亦然是什么意思,例如585a502 不能及时合并,因为它将是循环图(如果是关于拆分,例如 9a24ec6 被拆分为 3 个不相邻的提交)
    • @GuyCoder,是的 Git'-commits-graph 是一般 DAG 的一个子集,但唯一的区别是:Git 要求仅从 2 个提交合并(最多 2 个传入箭头进入一个节点)。我认为这是一个微小的区别(带有 n 个传入箭头的节点可以拆分为带有 2 个传入箭头的 n-1 个节点),Git 仍然可以用作 DAG 的示例。
    • @Renat 编辑没有增加任何价值(因此您也可以还原它)。让我澄清一下“非相邻文件之间的链接”是什么意思。这里有 3 个分支:master、root、CPlus 分支和create_tests_dir 分支。最后,create_tests_dir 合并为CPlusCPlus 合并为master。如果我还将master 合并到create_tests_dir11fb96d5c5b6e2 之间会显示什么?
    • @ivan_pozdeev,恕我直言,这样图表仍然是平面的。我添加了一个非平面 DAG 的示例。
    猜你喜欢
    • 2012-08-20
    • 1970-01-01
    • 2014-09-18
    • 1970-01-01
    • 1970-01-01
    • 2010-09-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多