为什么依赖图不表示为双向非循环图?

时间:2015-01-14 08:23:50

标签: algorithm data-structures graph dependencies

我知道依赖图(比如确定哪个包取决于安装期间的哪个包)可以表示为有向非循环图。

a
|--> b
|    |--> d
|    `--> e
|         |
|         |
`--> c <--'

例如,上图表示以下内容。

  • a取决于b,c,d,e
  • b取决于d,e,c
  • c完全取决于
  • d完全不依赖
  • e取决于c

该图可以帮助我们回答某个包在线性时间内所依赖的内容,即O(n)其中n是图中包和边的总数。示例:a依赖哪些包?结果是:b,c,d,e。

它可以帮助我们回答简单的问题,例如某个包在不断的时间内立即依赖什么。示例:哪个包立即依赖?结果是:b,c。

但它不能回答一个简单的问题,比如在一定时间内立即取决于某个包。示例:哪些包立即依赖于c?结果是:a和e。要回答这个简单的问题似乎需要对图形进行完整的搜索,从而需要线性时间。如果每个子顶点都保持与其父顶点的反向链接,同时仍保持子节点和父节点之间的区别,则可以改进这一点。

如果我们将每个子vertix的反向链接引入其父对象,它将成为双向非循环图,并且它似乎简化了许多图搜索算法。

我有以下问题。

  1. 这些依赖图是否有正式名称?
  2. 为什么我们常常在计算理论研究中看不到双向非循环图?
  3. 这些双向图是否用于依赖图的实际实现?实例

2 个答案:

答案 0 :(得分:4)

如果添加没有语义含义的反向链接并且仅加速“谁引用我”搜索,那么它仍然是DAG。同样,搜索树中的父链接不会将树转变为搜索“图形”。这是一个没有语义或数学意义的实现细节。因此,它没有单独研究(最多,在讨论复杂性时给出了一个副手提及)。

此外,无论如何,人们都可以灵活地使用边缘(依赖性 - >用户或用户 - >依赖性),两者都根据需要使用。我想不出在同一个图中需要两者的许多用例。即便如此,在需要时只需反转整个图形的边缘(单个O(n)操作)可能更有利可图。

由于这些原因,此优化通常不会给出单独的名称。它只是“一个DAG”,一个“(有支持)”,如果澄清那么重要。

答案 1 :(得分:2)

你在这里谈论的第一件事(当a-> b和b-> c时产生a-> c的所有边缘)被称为transitive closure。它本身是有用的,有趣的和研究的。但是,显式存储所有此类边缘将导致图形所需的存储空间(可能是二次方式)爆炸,因为在具有 | V | 节点的完整图形中,您具有 O(| V | 2 边缘。所以这是空间和时间复杂性之间的权衡:如果你存储所有(前向)边缘,你可以在你观察到的恒定时间内更快地遍历图形(向前),但是你要付出存储价格。 / p>

虽然你没有提出这个问题,但我要指出明确地存储传递闭包可能不适用于依赖图。以包管理器为例:您希望它能够快速找到直接的依赖关系,以检查它们是否已安装,以及它们是否可能将缺少的连接添加到安装多个包的事务中。但是,启用对包的所有(直接和间接)依赖关系的恒定时间访问在这种情况下似乎并不特别有用,因为无论如何大多数间接依赖关系都可能得到满足。你只需要获得一个更大的列表,然后可能会得出结论,大多数都是安装的。


您正在谈论的另一件事,即每条边都反转的图,称为transpose graph。请注意,如果同时存储&#34; direct&#34;则需要[bi]为边缘着色(使用不同名称的成员指针/引用)。和转置图在相同的数据结构中。以这种方式将它们存储在一起是相当微不足道的,所以我想这就是为什么你没有多少提及它。一些图算法工作/书籍do assume这种有向图的表示,即输入和输出边都存储在每个顶点的单独(双重链接)列表中。虽然许多(介绍性)教科书确实没有谈论它(可能是为了保持简单的演示),这种表示(即包括传入和传出列表)is used in practice, for example in LEDA。这是来自a LEDA presentation的幻灯片,详细说明了它们的静态(即假定的固定)图形数据结构;动态的将有双链表而不是数组。我包括单向(&#34;定向&#34;)和他们的&#34;双向&#34;表示以便于比较:

enter image description here

Boost有一个类似的功能,虽然它只是对adjacency list implementation的调整(称为bidirectionalS):

  

bidirectionalS选择器指定图形将提供in_edges()函数以及out_edges()函数。这会使每条边的空间开销增加两倍,这就是为什么in_edges()是可选的。

请记住,因为你可以区分两组边缘(通过&#34;第一个在&#34;和#34;首先输出&#34;在LEDA或in_edges()和out_edges的情况下在这里你真正拥有的是一个数学上disjoint union的有向图及其转置。如果你失去了两组边缘(指针)之间的区别/颜色,你所获得的有时被称为双向图,尽管这个术语(如图论中的很多)is unfortunately overloaded 。如果您希望LEDA的术语双向图以某种方式标准化其含义,那么它实际上是more likely to mean the same thing as bidirected graph to theorists

总结我到目前为止的答案:

  1. 我不认为这样存储的依赖图的名称,但对于一般双向表示的图形将是合理的名称,遗憾的是,它不会出现这样的名称除了一些软件包之外。双向(或双向)图是一个更广泛的术语,但大多数理论家可能认为你的意思是你不能再分辨出两组边之间的区别(即他们假设你的意思是联合而不是不相交联合转置图。)

  2. 在实际实施环境(如LEDA或提升)中,它似乎主要是一个方面,所以理论和介绍书似乎并不关心它。

  3. 对于包存储库(3)的实际表示,您似乎忽略了大多数(我知道)将存储AND,OR和NOT约束来另外处理替代和冲突。您只能像上面讨论的那样使用依赖图来处理AND。一旦你添加了那些额外的OR&amp;不是你需要更难解决的功能(NP-complete)SAT problems只是为了安装一些东西;见the Opium paper(2007)进行讨论;对于最近的一个(2010年),请参阅Apt-pbo paper。因此,相比之下,恒定时间反向依赖性查找似乎开始变得微不足道。但要真正回答你的问题:

    1. 我查看了apt源,它确实将反向依赖项分别存储在其缓存中(您query with apt-cache)。对于每个包(pkgCache::Package中定义的pkgcache.h),每次安装或删除某些内容时都会RevDepends linked list并更新它:在depcache.cc:pkgDepCache::Update中它有for循环做(除其他事项外)Update(P.ParentPkg().RevDependsList());