Master—Theorem 主定理的证明和使用
引言?
在分析算法的时候,我们经常需要分析递归算法的时间复杂度。Master——Theorem 正是用于快速得出递归算法时间复杂度的方法。
Master—Theorem
假设某个递归算法的时间复杂度递归公式为:
T
(
n
)
=
a
∗
T
(
n
b
)
+
n
d
T(n)=a*T(\frac{n}{b}) + n^{d}
T(n)=a∗T(bn)+nd,其中
a
>
1
,
b
>
1
,
d
>
0
a>1,b>1,d>0
a>1,b>1,d>0。
这个表达式其实是在表达:将规模为
n
n
n的问题转换为a个规模为
n
b
\frac{n}{b}
bn的子问题,在合并这些子问题的解时需要花费
O
(
n
d
)
O(n^{d})
O(nd)时间。
例如Merge-Sort算法里面,我们将规模为 n n n的排序问题先转换为 2 个规模为 n 2 \frac{n}{2} 2n的子问题,然后合并这两个子问题需要花费 O ( n ) O(n) O(n)的时间,因此其时间复杂度的递归公式为 T ( n ) = 2 T ( n 2 ) + O ( n ) T(n)=2T(\frac{n}{2})+O(n) T(n)=2T(2n)+O(n)。
那么 Master——Theorem是干嘛的呢?个人觉得,这就是主定理其实提供一种对形式为 T ( n ) = a ∗ T ( n b ) + n d T(n)=a*T(\frac{n}{b}) + n^{d} T(n)=a∗T(bn)+nd递推公式的快速求解Trick,从接下来的证明我们看到,这个主定理只是对我们的一般化证明过程做了一个小小的总结。
先看主定理的内容:
设某个递归算法的时间复杂度递归公式为:
T
(
n
)
=
a
∗
T
(
n
b
)
+
n
d
T(n)=a*T(\frac{n}{b}) + n^{d}
T(n)=a∗T(bn)+nd,其中
a
>
1
,
b
>
1
,
d
>
0
a>1,b>1,d>0
a>1,b>1,d>0。则有:
- 当 a < b d a<b^{d} a<bd,也就是 l o g b a < d log_{b}a < d logba<d 时, T ( n ) = O ( n d ) T(n)=O(n^{d}) T(n)=O(nd)
- 当 a = b d a=b^{d} a=bd,也就是 l o g b a = d log_{b}a=d logba=d 时, T ( n ) = O ( n l o g b a ∗ l o g n ) T(n)=O(n^{log_{b}a}*logn) T(n)=O(nlogba∗logn)
- 当 a > b d a>b^{d} a>bd,也就是 l o g b a > d log_{b}a>d logba>d 时, T ( n ) = O ( n l o g b a ) T(n)=O(n^{log_{b}a}) T(n)=O(nlogba)
现在,我们来证明一下。
我们知道,这种子问题的划分方式是将规模为n的问题,分解为a个问题规模为
n
b
\frac{n}{b}
bn的子问题。
对于第0层(最高层),合并子问题需要花费
n
d
n^{d}
nd的时间
对于第 1层(第一次划分出出来的子问题),共有 1 ∗ a 1*a 1∗a个子问题,每个子问题合并需要花费 ( n b ) d (\frac{n}{b})^{d} (bn)d,于是在第1层,合并一共需要花费 O ( a ∗ ( n b ) d ) O(a*(\frac{n}{b})^{d}) O(a∗(bn)d)时间,也就是 a b d ∗ n d \frac{a}{b^{d}}*n^{d} bda∗nd。
对于第2层。第2层需要对第1层的每个问题都划分为a个更小的子问题。因此,这一层一共有 a ∗ a a*a a∗a个子问题,而这每个子问题合并需要 O ( ( n b 2 ) d ) O((\frac{n}{b^{2}})^{d}) O((b2n)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} a2∗(b2n)d=(bda)2∗nd
类似的,我们发现第三层合并需要
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}
a3∗(b3n)d=(bda)3∗nd
···
···
最后第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}
ak∗(bkn)d=(bda)k∗nd,
那么这个最大的k是多少呢?我们知道,第k层中每个问题的规模是1,因此我们从下往上推,第k层每个问题规模为1,那么第k-1层 每个问题规模为b,第k-2层每个问题规模为
b
2
b^{2}
b2,一直最顶层问题规模为n为止。此时有:
b
∗
b
∗
b
∗
b
⋅
⋅
⋅
b
=
n
b*b*b*b···b=n
b∗b∗b∗b⋅⋅⋅b=n,即
b
k
=
n
b^{k}=n
bk=n,因此
k
=
l
o
g
b
n
k = log_{b}n
k=logbn
因此,一共有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)=nd+(bda)nd+(bda)2nd+(bda)3nd+⋅⋅⋅+(bda)knd
我们把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}
T(n)=(bda)0nd+(bda)1nd+(bda)2nd+(bda)3nd+⋅⋅⋅+(bda)knd
把各项的公因子
n
d
n^{d}
nd提出出来:
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})
T(n)=nd((bda)0+(bda)1+(bda)2+(bda)3+⋅⋅⋅+(bda)k)
ok,这看起来就是一个等比数列了哇。为了方便,我们令
q
=
a
b
d
q=\frac{a}{b^{d}}
q=bda,
那么:
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})
T(n)=nd(q0+q1+q2+q3+⋅⋅⋅+qk)
这就是一个等比数列求和问题,很明显,我们可以使用等比数列求和的方法来搞。
等式两边同时乘以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})
T(n)=nd(q0+q1+q2+q3+⋅⋅⋅+qk) ==>
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})
qT(n)=nd(q1+q1+q2+q3+⋅⋅⋅+qk+1)
上下相减,得到:
(
1
−
q
)
T
(
n
)
=
n
d
(
1
−
q
k
+
1
)
−
−
−
−
−
−
(
式
1
)
(1-q)T(n)=n^{d}(1-q^{k+1})------(式1)
(1−q)T(n)=nd(1−qk+1)−−−−−−(式1)
此时我们需要对q分情况讨论:
如果
q
=
1
q=1
q=1.
那么会得到
0
=
0
0=0
0=0这样的恒等式。此时等比数列错位相减法失效。考虑到q=1,说明这个数列每一项都一样,而第一项为
q
0
q^{0}
q0,换句话说,这个等比数列的和其实就是
k
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)=nd∗k=nd∗logbn,使用换底公式:
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}}
T(n)=nd∗k=nd∗log2blog2n
当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})
T(n)=nd∗log2blog2n<nd∗log22log2n=O(ndlog2n)
如果
q
!
=
1
q!=1
q!=1.
那么
1
−
q
!
=
0
1-q!=0
1−q!=0,于是将式1的左右两边同除以
1
−
q
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}
T(n)=1−qnd(1−qk+1)
当
q
<
1
q<1
q<1时,即
a
b
d
<
1
\frac{a}{b^{d}} < 1
bda<1。当k很大时
q
k
+
1
q^{k+1}
qk+1趋于0,所以
1
−
q
k
+
1
1-q^{k+1}
1−qk+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)≈nd∗1−q1=c∗nd,也就是说
T
(
n
)
=
O
(
n
d
)
T(n)=O(n^{d})
T(n)=O(nd).
当 q > 1 q>1 q>1时,即 a b d > 1 \frac{a}{b^{d}} > 1 bda>1。当k很大时, q k + 1 q^{k+1} qk+1趋于无穷, 1 − q k + 1 1-q^{k+1} 1−qk+1趋于 − q k + 1 -q^{k+1} −qk+1,所以主要项就是 q k q^{k} qk, T ( n ) ≈ n d ∗ q k T(n)\approx n^{d} *q^{k} T(n)≈nd∗qk.
将
k
=
l
o
g
b
n
,
q
=
a
b
d
k=log_{b}{n},q=\frac{a}{b^{d}}
k=logbn,q=bda代入。因此有
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)≈nd∗qk=nd∗(bda)logbn=nd∗bdlogbnalogbn=nd∗(blogbn)dalogbn=nd∗ndalogbn=alogbn
于是
T
(
n
)
=
O
(
a
l
o
g
b
n
)
T(n)=O(a^{log_{b}{n}})
T(n)=O(alogbn),然而这个好像和定理不太一样,我们需要使用到一个对数的运算性质。
即:
a
l
o
g
b
n
=
n
l
o
g
b
a
a^{log_{b}n}=n^{log_{b}a}
alogbn=nlogba
证明过程很简单,就是使用对数的实际含义。
假设
b
x
=
n
b^{x}=n
bx=n,那么
x
=
l
o
g
b
n
x=log_{b}n
x=logbn,x就是这个对数的含义。
同时假设
b
y
=
a
b^{y}=a
by=a,那么
y
=
l
o
g
b
a
y=log_{b}a
y=logba.
于是,左边
a
l
o
g
b
n
a^{log_{b}n}
alogbn可以写成:
(
b
y
)
x
(b^{y})^{x}
(by)x;
而右边可以写成
(
b
x
)
y
(b^{x})^{y}
(bx)y.显然,
(
b
y
)
x
(b^{y})^{x}
(by)x=
(
b
x
)
y
(b^{x})^{y}
(bx)y,于是,左边等于右边。
因此
a
l
o
g
b
n
=
n
l
o
g
b
a
a^{log_{b}n}=n^{log_{b}a}
alogbn=nlogba是成立的。
所以
T
(
n
)
=
O
(
a
l
o
g
b
n
)
T(n)=O(a^{log_{b}{n}})
T(n)=O(alogbn),可以写成:
T
(
n
)
=
O
(
n
l
o
g
b
a
)
T(n)=O(n^{log_{b}{a}})
T(n)=O(nlogba).
到此为止,主定理就证明完毕。
我们观察整个证明过程,其实就是对证明过程做了一个小小的归纳总结。在实际应用中,如果忘记主定理,那么直接展开代入实际数值推一遍就可以了。
主定理的使用
主定理可以用于快速得到时间复杂度为递归公式的上界解,下面举几个栗子。
1.
T
(
n
)
=
3
T
(
n
2
)
+
c
n
T(n)=3T(\frac{n}{2})+cn
T(n)=3T(2n)+cn
那么:
a
=
3
,
b
=
2
,
d
=
1
a=3,b=2,d=1
a=3,b=2,d=1,
l
o
g
2
3
>
1
log_{2}{3}>1
log23>1,所以
T
(
n
)
=
O
(
n
l
o
g
2
3
)
T(n)=O(n^{log_{2}3})
T(n)=O(nlog23)
2.
T
(
n
)
=
2
T
(
n
2
)
+
c
n
2
T(n)=2T(\frac{n}{2})+cn^{2}
T(n)=2T(2n)+cn2
那么:
a
=
2
,
b
=
2
,
d
=
2
a=2,b=2,d=2
a=2,b=2,d=2,
l
o
g
2
2
<
2
log_{2}2<2
log22<2,所以:
T
(
n
)
=
O
(
n
2
)
T(n)=O(n^{2})
T(n)=O(n2)
上面两个,不使用主定理,直接展开也可以得到一样的结果。
更多推荐
所有评论(0)