USING INDUCTION TO DESIGN 使用归纳法设计算法 [6/14]

接上文:http://blog.csdn.net/jj12345jj198999/article/details/6610971

 

拓扑排序【Q4】


假设这里有一系列的任务,这些任务每次只能执行其中一个。其中有一些任务要依赖其他任务的完成,如果其他任务没有完成则这些任务不可能开始执行。已知所有的依赖关系,我们想找到一个符合依赖关系的任务安排计划表。(即每一个被计划执行的任务只有在它依赖的任务全部完成时才可以开始执行)我们想要设计一种能够迅速产生出这么一个计划表的算法。该问题也就是拓扑排序。任务和他们的依赖关系能够通过一幅有向图得到展现。一个有向图拥有一个顶点的集合V(与例子中的任务相对应),以及一个由一对顶点构成的边的集合E。如果一个与v对应的任务必须在一个与w对应的任务之前执行,那么图中就存在一条从v到w的边(表示(v,w)∈E)。该图一定为无环图,否则环上的任务将永远不可能开始执行。这里给出了使用图来说明问题的简单描述。

 

问题:给定一个有n个顶点的有向无环图G=(V,E),这些顶点从1到n进行编号,因此如果顶点v被标记为k,那么顶点v可以通过一条直接路径到达所有编号大于k的顶点。(一条路径是一系列点的序列,这些点v1,v2...vk通过边(v1,v2),(v2,v3)...(vk-1,vk)相连)

 

我们再一次尝试依据规模更小的问题来解决这个问题。考虑一个更小问题的直接做法是移去一个顶点。换句话说,通过下面的方法可以看到归纳法蕴含在顶点的数量之中。

归纳假设1:我们已经知道如何按照上面描述的条件用n-1个顶点为所有的图做上标记。

 

只有一个顶点的最基本的情况是很简单的。如果n>1,我们可以移去一个顶点,然后尝试使用归纳假设,再尝试去拓展标记。我们首先需要核实的一点是移除一个顶点后的问题和原来的问题是一样的(除了更小的规模外)。拥有一个一样的问题很必要,否则归纳假设将无法使用。例子中唯一假设是图不是循环图。由于移除一个顶点不可能产生一个循环图,那么这种缩减是可行的。

 

这种缩减的问题在于尽管假设可以使用但我我们不清楚如何去拓展解决方法,即怎么去标记移除的顶点。我们解决该问题的方案是精心选择顶点v。由于移去任何一个顶点产生的规模更小的问题都是符合要求的,所以我们可以任意选择一个顶点作为第n个顶点。因此,我们应该移除那些最容易获取标记的点。一个明显的选择是那些没有依赖关系的顶点(任务),也就是该顶点的入度(指向该点的边数)为0。可以把该顶点标记为1,这不会带来什么错误。

 

但是,我们总能找到一个入度为0的点么?答案按直觉是肯定的,但我们必须从某个地方开始。毫无疑问的是,如果所有的顶点都有大于0的入度我们可以用各种方式遍历图而且永远不会停止。但是由于只有有限数量的顶点需要我们去循环查找,这与我们的假设是相矛盾的。(同理我们也可以从出度为0的顶点开始寻找,然后把该顶点标记为n)剩下的算法就很清晰了。移除选择的顶点的相邻边,剩下的图依然是无环图,再用2到n标记图的剩余部分。由于我们需要用2到n而不是1到n-1标记剩下的图,我们需要对归纳假设做点改动。

 

归纳假设2:我们知道依据问题的条件如何使用n-1不同标记的集合去标记一个拥有n-1个顶点的图。

 

下面给出与之对应的算法

拓扑排序算法
{G={V,E}:一个有向无环图};
begin
   对所有的顶点的入度初始化;
   {可以通过深度优先搜索}
   G_label:=0;
   for i:=1 to n do
      if vi.indegree=0 then put vi in Queue;
   repeat
      remove vertex v from Queue;
      G_label:=G_label + 1;
      v_label:=G_label;
      for all edges(v,w) do
        w.indegree:=w.ingegree-1;
        if w.indegree=0 then put w in Queue
    until Queue is empty
end;

复杂度:初始化入度计数器需要O(|V|+|E|)时间(例如在例子中使用深度优先搜索)。找到一个入度为0的点要花费常数时间(访问队列)。每一条边(v,w)被考虑一次(当v从队列中被取出时)。因此,计数器需要更新的次数的多少就是图中边数目的多少。因此算法的执行时间就是输入规模的线性时间O(|V|+|E|)。

 

总结:这是另一个使用归纳法直接设计算法的例子。这里的技巧在于明智选择归纳序列。我们不是武断地缩减问题规模,而是选择移除一个特殊的顶点。任何给定的问题的规模都可以用很多可能的方法加以缩减。思想就在于探寻各种各样的选择然后测试产生的算法。我们从多项式计算这个例子中看到从左向右要比从右向左好。另一个通常的可能性是对从上往下和从下往上进行比较。同样也可能每次递增2而不是递增1,当然还有更多的可能情况。有时最好的归纳序列甚至对于所有的输入来说也是不尽相同的。设计一个特殊的算法去寻找一个对问题执行规模缩减的最好的方法有时是值得的。

你可能感兴趣的:(USING INDUCTION TO DESIGN 使用归纳法设计算法 [6/14])