Master—Theorem 主定理的证明和使用

版权声明:本文为博主jmh原创文章,未经博主允许不得转载。 https://blog.csdn.net/jmh1996/article/details/82827579

引言?

在分析算法的时候,我们经常需要分析递归算法的时间复杂度。Master——Theorem 正是用于快速得出递归算法时间复杂度的方法。

Master—Theorem

假设某个递归算法的时间复杂度递归公式为: T ( n ) = a T ( n b ) + n d T(n)=a*T(\frac{n}{b}) + n^{d} ,其中 a > 1 , b > 1 , d > 0 a>1,b>1,d>0
这个表达式其实是在表达:将规模为 n n 的问题转换为a个规模为 n b \frac{n}{b} 的子问题,在合并这些子问题的解时需要花费 O ( n d ) O(n^{d}) 时间。

例如Merge-Sort算法里面,我们将规模为 n n 的排序问题先转换为 2 个规模为 n 2 \frac{n}{2} 的子问题,然后合并这两个子问题需要花费 O ( n ) O(n) 的时间,因此其时间复杂度的递归公式为 T ( n ) = 2 T ( n 2 ) + O ( n ) T(n)=2T(\frac{n}{2})+O(n)

那么 Master——Theorem是干嘛的呢?个人觉得,这就是主定理其实提供一种对形式为 T ( n ) = a T ( n b ) + n d T(n)=a*T(\frac{n}{b}) + n^{d} 递推公式的快速求解Trick,从接下来的证明我们看到,这个主定理只是对我们的一般化证明过程做了一个小小的总结。

先看主定理的内容:
设某个递归算法的时间复杂度递归公式为: T ( n ) = a T ( n b ) + n d T(n)=a*T(\frac{n}{b}) + n^{d} ,其中 a > 1 , b > 1 , d > 0 a>1,b>1,d>0 。则有:

  • a < b d a<b^{d} ,也就是 l o g b a < d log_{b}a < d 时, T ( n ) = O ( n d ) T(n)=O(n^{d})
  • a = b d a=b^{d} ,也就是 l o g b a = d log_{b}a=d 时, T ( n ) = O ( n l o g b a l o g n ) T(n)=O(n^{log_{b}a}*logn)
  • a > b d a>b^{d} ,也就是 l o g b a > d log_{b}a>d 时, T ( n ) = O ( n l o g b a ) T(n)=O(n^{log_{b}a})

现在,我们来证明一下。
我们知道,这种子问题的划分方式是将规模为n的问题,分解为a个问题规模为 n b \frac{n}{b} 的子问题。
对于第0层(最高层),合并子问题需要花费 n d n^{d} 的时间

对于第 1层(第一次划分出出来的子问题),共有 1 a 1*a 个子问题,每个子问题合并需要花费 ( n b ) d (\frac{n}{b})^{d} ,于是在第1层,合并一共需要花费 O ( a ( n b ) d ) O(a*(\frac{n}{b})^{d}) 时间,也就是 a b d n d \frac{a}{b^{d}}*n^{d}

对于第2层。第2层需要对第1层的每个问题都划分为a个更小的子问题。因此,这一层一共有 a a a*a 个子问题,而这每个子问题合并需要 O ( ( n b 2 ) d ) O((\frac{n}{b^{2}})^{d}) 时间。于是这一层合并需要花费: a 2 ( n b 2 ) d = ( a b d ) 2 n d a^{2}*(\frac{n}{b^{2}})^{d} =(\frac{a}{b^{d}})^{2}*n^{d}

类似的,我们发现第三层合并需要 a 3 ( n b 3 ) d = ( a b d ) 3 n d a^{3}*(\frac{n}{b^{3}})^{d} =(\frac{a}{b^{d}})^{3}*n^{d}
···
···
最后第k成的合并需要 a k ( n b k ) d = ( a b d ) k n d a^{k}*(\frac{n}{b^{k}})^{d} =(\frac{a}{b^{d}})^{k}*n^{d} ,
那么这个最大的k是多少呢?我们知道,第k层中每个问题的规模是1,因此我们从下往上推,第k层每个问题规模为1,那么第k-1层 每个问题规模为b,第k-2层每个问题规模为 b 2 b^{2} ,一直最顶层问题规模为n为止。此时有: b b b b b = n b*b*b*b···b=n ,即 b k = n b^{k}=n ,因此 k = l o g b n k = log_{b}n

因此,一共有k层。

ok,我们将各层合并消耗的时间累加起来就是T(n)了。
T ( n ) = n d + ( a b d ) n d + ( a b d ) 2 n d + ( a b d ) 3 n d + + ( a b d ) k n d T(n)=n^{d}+(\frac{a}{b^{d}})n^{d}+(\frac{a}{b^{d}})^{2}n^{d}+(\frac{a}{b^{d}})^{3}n^{d}+···+(\frac{a}{b^{d}})^{k}n^{d}
我们把T(n)写更好看一些:
T ( n ) = ( a b d ) 0 n d + ( a b d ) 1 n d + ( a b d ) 2 n d + ( a b d ) 3 n d + + ( a b d ) k n d T(n)=(\frac{a}{b^{d}})^{0}n^{d}+(\frac{a}{b^{d}})^{1}n^{d}+(\frac{a}{b^{d}})^{2}n^{d}+(\frac{a}{b^{d}})^{3}n^{d}+···+(\frac{a}{b^{d}})^{k}n^{d}
把各项的公因子 n d n^{d} 提出出来:
T ( n ) = n d ( ( a b d ) 0 + ( a b d ) 1 + ( a b d ) 2 + ( a b d ) 3 + + ( a b d ) k ) T(n)=n^{d}((\frac{a}{b^{d}})^{0}+(\frac{a}{b^{d}})^{1}+(\frac{a}{b^{d}})^{2}+(\frac{a}{b^{d}})^{3}+···+(\frac{a}{b^{d}})^{k})

ok,这看起来就是一个等比数列了哇。为了方便,我们令 q = a b d q=\frac{a}{b^{d}}
那么:
T ( n ) = n d ( q 0 + q 1 + q 2 + q 3 + + q k ) T(n)=n^{d}(q^{0}+q^{1}+q^{2}+q^{3}+···+q^{k})
这就是一个等比数列求和问题,很明显,我们可以使用等比数列求和的方法来搞。
等式两边同时乘以q,得到:
T ( n ) = n d ( q 0 + q 1 + q 2 + q 3 + + q k ) T(n)=n^{d}(q^{0}+q^{1}+q^{2}+q^{3}+···+q^{k}) ==>
q T ( n ) = n d ( q 1 + q 1 + q 2 + q 3 + + q k + 1 ) qT(n)=n^{d}( q^{1}+q^{1}+q^{2}+q^{3}+···+q^{k+1})
上下相减,得到:
( 1 q ) T ( n ) = n d ( 1 q k + 1 ) ( 1 ) (1-q)T(n)=n^{d}(1-q^{k+1})------(式1)
此时我们需要对q分情况讨论:
如果 q = 1 q=1 .
那么会得到 0 = 0 0=0 这样的恒等式。此时等比数列错位相减法失效。考虑到q=1,说明这个数列每一项都一样,而第一项为 q 0 q^{0} ,换句话说,这个等比数列的和其实就是 k k (因为有K项)。
于是: T ( n ) = n d k = n d l o g b n T(n)=n^{d}*k=n^{d}*log_{b}{n} ,使用换底公式: T ( n ) = n d k = n d l o g 2 n l o g 2 b T(n)=n^{d}*k=n^{d}*\frac{log_{2}{n}}{log_{2}{b}}
当b>=2时: T ( n ) = n d l o g 2 n l o g 2 b < n d l o g 2 n l o g 2 2 = O ( n d l o g 2 n ) T(n)=n^{d}*\frac{log_{2}{n}}{log_{2}{b}}<n^{d}*\frac{log_{2}{n}}{log_{2}{2}}=O(n^d log_{2}{n})
如果 q ! = 1 q!=1 .
那么 1 q ! = 0 1-q!=0 ,于是将式1的左右两边同除以 1 q 1-q ,得到:
T ( n ) = n d ( 1 q k + 1 ) 1 q T(n)=\frac{n^{d}(1-q^{k+1})}{1-q}
q < 1 q<1 ,即 a b d < 1 \frac{a}{b^{d}} < 1 。当k很大时 q k + 1 q^{k+1} 趋于0,所以 1 q k + 1 1-q^{k+1} 趋于1。所以有 T ( n ) n d 1 1 q = c n d T(n)\approx n^{d} *\frac{1}{1-q}=c*n^{d} ,也就是说 T ( n ) = O ( n d ) T(n)=O(n^{d}) .

q > 1 q>1 ,即 a b d > 1 \frac{a}{b^{d}} > 1 。当k很大时, q k + 1 q^{k+1} 趋于无穷, 1 q k + 1 1-q^{k+1} 趋于 q k + 1 -q^{k+1} ,所以主要项就是 q k q^{k} T ( n ) n d q k T(n)\approx n^{d} *q^{k} .

k = l o g b n , q = a b d k=log_{b}{n},q=\frac{a}{b^{d}} 代入。因此有 T ( n ) n d q k = n d ( a b d ) l o g b n = n d a l o g b n b d l o g b n = n d a l o g b n ( b l o g b n ) d = n d a l o g b n n d = a l o g b n T(n)\approx n^{d} *q^{k}=n^{d}* (\frac{a}{b^{d}})^{log_{b}n}=n^{d}*\frac{a^{log_{b}n}}{b^{dlog_{b}n}}=n^{d}*\frac{a^{log_{b}n}}{(b^{log_{b}n})^{d}}=n^{d}*\frac{a^{log_{b}n}}{n^{d}}=a^{log_{b}n}
于是 T ( n ) = O ( a l o g b n ) T(n)=O(a^{log_{b}{n}}) ,然而这个好像和定理不太一样,我们需要使用到一个对数的运算性质。

即: a l o g b n = n l o g b a a^{log_{b}n}=n^{log_{b}a}
证明过程很简单,就是使用对数的实际含义。

假设 b x = n b^{x}=n ,那么 x = l o g b n x=log_{b}n ,x就是这个对数的含义。
同时假设 b y = a b^{y}=a ,那么 y = l o g b a y=log_{b}a .

于是,左边 a l o g b n a^{log_{b}n} 可以写成: ( b y ) x (b^{y})^{x} ;
而右边可以写成 ( b x ) y (b^{x})^{y} .显然, ( b y ) x (b^{y})^{x} = ( b x ) y (b^{x})^{y} ,于是,左边等于右边。
因此 a l o g b n = n l o g b a a^{log_{b}n}=n^{log_{b}a} 是成立的。

所以 T ( n ) = O ( a l o g b n ) T(n)=O(a^{log_{b}{n}}) ,可以写成: T ( n ) = O ( n l o g b a ) T(n)=O(n^{log_{b}{a}}) .
到此为止,主定理就证明完毕。

我们观察整个证明过程,其实就是对证明过程做了一个小小的归纳总结。在实际应用中,如果忘记主定理,那么直接展开代入实际数值推一遍就可以了。

主定理的使用

主定理可以用于快速得到时间复杂度为递归公式的上界解,下面举几个栗子。

1. T ( n ) = 3 T ( n 2 ) + c n T(n)=3T(\frac{n}{2})+cn
那么: a = 3 , b = 2 , d = 1 a=3,b=2,d=1 , l o g 2 3 > 1 log_{2}{3}>1 ,所以 T ( n ) = O ( n l o g 2 3 ) T(n)=O(n^{log_{2}3})
2. T ( n ) = 2 T ( n 2 ) + c n 2 T(n)=2T(\frac{n}{2})+cn^{2}
那么: a = 2 , b = 2 , d = 2 a=2,b=2,d=2 , l o g 2 2 < 2 log_{2}2<2 ,所以: T ( n ) = O ( n 2 ) T(n)=O(n^{2})

上面两个,不使用主定理,直接展开也可以得到一样的结果。

猜你喜欢

转载自blog.csdn.net/jmh1996/article/details/82827579