Resolving Recurrence

(1) – Substitution Method

 

整个recurrence就是研究一个数学表达式:T(n) = aT(n/b)+f(n)   意思是先有一个问题T(n), 解决思路为把T(n)切分成a个小问题, 每个小问题的代价是T(n/b), 然后把a个T(n/b)的结果合并起来的代价是f(n);依次类推,直到T(k)的粒度小到其复杂度是一个常数。很像是递归吧~~递归只是一种实现方式,这里介绍的是一种思路和评估这种思路复杂度的方法。

经典的评估Recurrence的方法有3种,Substitutuon, Recursion Tree和Master Method.  看下面的命题:

T(n) = 4T(n/2) + n ,需要证明T(n)=O(n2 )

Substitution:

假设T(k) <= ck k<n; 所以 T(n) <= 4(n/2)2 +n = n2 +n 要证明n2 +n<=cn2 好像不容易。

我们换个假设,假设T(k)<=c1 k2 -c2 k ,这样的话T(n) <= c1 n2 -(2c2 -1)n = c1 n2 -c2 n-(c2 -1)n. 所以只要 c2 >= 1,就证明成功了。注意这里,似乎没有对c1 任何限制。其实不是的!对于所有的induction方法来说,我们都需要一个初始条件,T(n0 ) = H(1) <=c1 n0 2 -c2 n0 这里我使用H(1) 代表常数复杂度。为了满足初始条件,我们需要恰当的选择 c1 ,c2 和n0

其实这里的初始条件就对应了算法设计里面的Base Case。Base Case就是将问题切分到最后那个粒度最小的复杂度为常数的Case。就上面的那个例子来说,c1 和c2 ,对运行时起主导作用的是前者,你在实现时设计的Base Case能使c1 越小,程序运行越快。

 

(2) – Recursion Tree

 

T(n) = T(n/3) + T(2n/3) + n

Recursion Tree其实就是Iteration,只不过图型化以后我们看起来更容易理解了。

T(n) = T(n/3) + T(2n/3) + n
       = [T(n/9) + T(2n/9) + n/3] + [T(2n/9) + T(4n/9) + 2n/3]
       = ……
画成图就是:

 

总的cost就是把每一层的cost加起来。层数就是树的高度。上面的这个问题有点tricky的。从图上看分两种情况,一种以1/3的方式减少n,还有一种是以2/3的方式。前一种方式经过log3 n次就到达叶子节点,后一种方式经过log3/2 n次,因为log3/2 n > log3 n,树的高度为log3/2 n。每一层加起来的代价是cn, 其实越到下面,每一层的cost会小于cn的,因为那时候按照1/3的方式减少的那一枝已经不存在了。

T(n) <= cn x log3/2 n = O(nlgn)

Recursion tree很直观,我们可以用它来帮助我们猜测规律,然后再用Substitution方法Double Confirm。

不过无论是Substitution还是Recursion Tree都比较烦,不能马上得出结果。有人总结了规律,叫做Mater Method。使用Master Method, 我们很快就可以直接写出T(n)=aT(n/b)+f(n)的时间复杂度。

 

(3) – Master Method

 

 

前面分别介绍了 Resolving Recurrence(1) – Substitution Method 和 Resolving Recurrence(2) – Recursion Tree 来评估
T(n) = aT(n/b) + f(n) 的时间复杂度。

这一节介绍Master Method,既然叫Master,可见其重要程度。掌握了Master Method,我们可以很快写出Merge Sort, Heap Sort以及Quick Sort的时间复杂度。

Master Method:   (在 a>=1 并且 b>1 的情况下)

  • 如果f(n) is polynomially smaller than nlog b a , polynomially smaller就是grow slower, 数学表达式f(n)=O(nlog b a-e ), e>0。 T(n) = O(nlog b a )
  • 如果f(n) is polynomially same as nlog b a , T(n) = O(nlog b a lgn)
  • 如果f(n) is polynomially faster than  nlog b a , 并且 af(n/b) <= cf(n),  c<1 && n>=b , T(n) = H(f(n)) . H表示复杂度相当。

举个例子T(n)=4T(n/2)+n , 其中a=4, b=2, –> nlog b a =  n2 , 而f(n)=n, 属于第一种情况所以 T(n) = O(n2 )。 如果T(n)=2T(n/2)+n, 则属于第二种情况,T(n) = O(n lgn).

在算法导论中有详细的证明,太符号化的东西往往难以记忆,下面我们用recursion tree的方式,帮助理解和记忆。

可见, 第一层有一个节点,第二层有a的节点,第 i 层有ai 个节点,所以到叶子节点那层,叶子的个数就是 a的 树高 次幂。而树高=logb n , 我们可以很容易证明 alog b n = nlog b a 。所以一共有nlog b a 个叶子,每个叶子其实就是我们的initial condition,就是我们的base case,是整个算法的基石。

cost of all the leaves nodes = H(nlog b a )

现在对照recursion tree再来回顾一下Master Method 的 3 种情况:

1. 当所有叶子的代价比根节点高时, O(nlog b a ) > f(n), 从root到leaves,每层的cost在geometry increase,由叶子主导整个算法的时间复杂度,所以 T(n) = O(nlog b a )

2. 当所有叶子的代价和根节点相当时,表示从root 到 leaves的每一层的cost 是 几乎balance的,总代价应该是 每一层代价乘以树高, T(n) = nlog b a x logb n = H(f(n) x logb n = O(nlog b a lgn)。

3. 当所有叶子的代价比根节点低时,由根节点主导整个算法的时间复杂度。af(n/b) <= cf(n),  c<1 && n>=b 表示把任务T(n)切分成a个T(n/b)的过程中,必须保证a个T(n/b)的合并代价必须小于上层任务的合并代价,并且每次切分,其单个节点的合并代价都是在减少的。简单来说就是 从root 到leaves的过程中,代价是geometry decrease的。 结论是 T(n) = H(f(n))。

 

 

你可能感兴趣的:(c,算法,tree,任务,n2,recursion)