参考文章:
李卿. 递归算法分析中主定理的应用[J]. 黑龙江科技信息, 2011(29):97+207.
Thomas H.Cormen,Charles E.Leiserson,Ronald L.Rivest,Clifford Stein. 殷建平等译. 算法导论第三版 [M]. 北京:机械工业出版社,2013,55-58.
前言:
本篇文章与我的 博客园 同步更新。
在此之前,请先阅读 【洛谷日报#33】时空复杂度分析及master定理,其中关于时间复杂度表示的基础知识不再阐述。
引出:
现在考虑一个问题:假设某算法的计算时间表示为递归式:
求该算法的时间复杂度。
当给你抛出这么一个题型时,你怎么办?
凭经验和感觉蒙
小几率能蒙对,但你觉得这种题CCF会送你分吗?
递归进去
像这样递归进去:
每项都 (ndiv 2),总共递归 (log_2 n) 层:
即 (T(n)=O(nlog^2 n))。
这么做不是没有道理,但是如果 (T(n)=3T(frac{n}{4})+nlog n),用这种方法根本算不出来(或许可以算出来,但是操作极其麻烦)。
主定理(master定理)求解
主定理:(a,b) 是常数,(f(n)) 为额外附加值函数(T(n)) 为递归式 (T(n)=aT(frac{n}{b})+f(n)quad(a>0,b>1)),就有:
- 当 (f(n)=O(n^{(log_ba)-epsilon})) 其中 (epsilon>0) 是一个常数(相当于 (log_ba>f(n))),则有 (T(n)=Theta(n^{log_ba}));
- 当 (f(n)=Theta(n^{log_ba})),则有 (T(n)=Theta(n^{log_ba}log n));
- 当 (f(n)=Omega(n^{(log_ba)+epsilon})) 其中 (epsilon>0) 是一个常数(相当于 (log_ba<f(n))),且对于一个常数 (c<1) 和所有足够大的 (n) 有 (af(frac{n}{b})leq cf(n))(这一条在这里可以暂时忽略不看,但在证明时起到至关重要的作用),则有 (T(n)=Theta(f(n))).
- 当 (f(n)=Theta(n^{log_ba}log^kn)) 其中 (kgeq1) 是一个常数,则有 (T(n)=Theta(n^{log_ba}log^{k+1}n));
这么看着有点枯燥乏味的样子,不利于理解,但如果丢掉定理四的话(毕竟CSP/NOIp好像真的没考过定理四,其实可以发现定理二和定理四其实是同一种,便于萌新理解就分开了),主定理的定义可以直接写成:
(图一)
也就是 (n^{log_ba}) 与 (f(n)) 进行比较!
图一、算法导论和上面提过的论文没有提到过定理四,但是原来那篇日报(#33)有,对此我想说,洛谷日报真是太好了!导论不行!日报行!(老伏拉夫了
举例说明:
例一:(T(n)=4T(frac{n}{2})+n),此时 (a=4,b=2,epsilon=1),那么 (log_ba=log_24=2,f(n)=O(n^{log_ba-epsilon})=O(n^{2-1})),(f(n)) 成立,所以 (T(n)=Theta(n^{log_ba})=Theta(n^2))。
例二:(T(n)=2T(frac{n}{2})+n),此时 (a=2,b=2),那么 (log_ba=log_22=1,f(n)=Theta(n^{log_ba})=Theta(n)),(f(n)) 成立,所以 (T(n)=Theta(n^{log_ba})=Theta(n))。
例三:(T(n)=4T(frac{n}{2})+n^3),此时 (a=4,b=2,epsilon=1),那么 (log_ba=log_24=2,f(n)=Omega(n^{log_ba+epsilon})=Omega(n^{2+1})),对于 (c=frac{2}{3}) 和够大的 (n),(left(af(frac{n}{b})=4(frac{n}{2})^3=4(frac{n^3}{8})=frac{n^3}{2} ight)leq left(cf(n)=frac{2n^3}{3} ight)),(f(n)) 成立,所以 (T(n)=Theta(f(n))=Theta(n^3))。
例二:(T(n)=2T(frac{n}{2})+nlog n),此时 (a=2,b=2,k=1),那么 (log_ba=log_22=1,f(n)=Theta(n^{log_ba}+log^kn)=Theta(nlog n)),(f(n)) 成立,所以 (T(n)=Theta(n^{log_ba}log^{k+1}n)=Theta(nlog^2 n))。
证明:
先声明:证明又长又臭,有亿点点难理解,学有余力的 dalao 可以来康康。
俗话说得好:“欲要证明master,就先画棵递归树”:
(图二)
关于图二的解释及证明:
对于第 (i) 层((i e log_bn)),有 (a^i) 个节点,而每个节点的值是 (f(frac{n}{b^i})),那么第 (i) 层总共的值是 (a_if(frac{n}{b^i}))。
对于第 (log_bn) 层,有 (a^{log_bn}) 个节点,而每个节点的时间复杂度是 (Theta(1)),那么这一层总共的时间复杂度是 (Theta(a^{log_bn})=Theta(n^{log_ba})) 层。
对于 (a^{log_bn}=n^{log_ba}) 的证明:
(a^{log_bn}=n^{log_ba}) 得证。
这么看,图二的递归树的总时间复杂度为 (T(n)=Theta(n^{log_ba})+sum_{j=0}^{log_bn-1}a^jf(frac{n}{b^j})),就是叶子节点层加上其它结点层的值。
证明*:
根据上文这个 (T(n)) 的时间复杂度,我们定义一个函数 (g(n)):
这个 (g(n)) 有一些性质:
- 当 (f(n)=O(n^{(log_ba)-epsilon})) 其中 (epsilon>0) 是一个常数,则有 (g(n)=O(n^{log_ba}));
- 当 (f(n)=Theta(n^{log_ba})) 时,则有 (g(n)=Theta(n^{log_ba}log n));
- 当 (f(n)=Omega(n^{(log_ba)+epsilon})) 其中 (epsilon>0) 是一个常数,且对于一个常数 (c<1) 和所有足够大的 (n) 有 (af(frac{n}{b})leq cf(n)),则有 (g(n)=Theta(f(n))).
- 当 (f(n)=Theta(n^{log_ba}log^kn)) 其中 (kgeq1) 是一个常数,则有 (g(n)=Theta(n^{log_ba}log^{k+1}n));
欸!有没有发现好像在哪见过!那是因为这是我从上文复制下来的(
证明关于g函数性质*:
性质 1:
将 (f(n)=O(n^{(log_ba)-epsilon})) 代入进 (g(n)=sum_{j=0}^{log_bn-1}a^jf(frac{n}{b^j})):
然后根据等比数列求和公式化简 (sum_{j=0}^{log_bn-1}(b^epsilon)^j):
这里回想一下 (b,epsilon) 的定义,(做到不忘初心牢记使命),它们是常数,所以式子中 (frac{1}{b^{epsilon}-1}) 也是常数,应忽略:
性质 1 得证。
性质 2:
性质 1 和性质 2 操作一样,就是一直代入再一直化简,将 (f(n)=Theta(n^{log_ba})) 代入进 (g(n)=sum_{j=0}^{log_bn-1}a^jf(frac{n}{b^j})):
注意:这里重头戏来了!式子中的 (log_bn) 是可以直接改底数的,因为可以通过换底公式得到对数函数都是同级的。因此我们可以把 (b) 改成 (2)(即 (log_bn) 化成 (log n)):
性质 2 得证。
性质 3:
性质 3 是最特殊的,用 (af(frac{n}{b})leq cf(n)) 递归 (j) 次可以得到 (a^j f(frac{n}{b^j})leq c^j f(n))。再用这个式子套 (g(n)) 定义:
解释一下上面的式子,因为 (c) 都是正数,所以如果 (g(n)leq f(n)sum_{j=0}^{log_bn-1}c^j),显然 (g(n)leq f(n)sum_{j=0}^{infty}c^j)。而这么做是为了更好证明。
接下来还是用等比数列求和公式化简:
你可能好奇这个等比数列求和是怎么化的,下面证明一下:
设 (S=c^0+c^1+c^2+cdots+c^{infty}) 表示 (sum_{j=0}^{infty}c^j),此时公比为 (c):
(cS=c^1+c^2+c^3+cdots+c^{infty}),这里 (infty+1=infty)。
((1-c)S=c^0=1)
(S=frac{1}{1-c})
即 (sum_{j=0}^{infty}c^j=frac{1}{1-c})。
回到题目,由 (g(n)leqleft(frac{1}{1-c} ight)f(n)) 得到 (g(n)=O(f(n)))。
而根据 (g(n)) 的定义 (g(n)=f(n)+af(frac{n}{b})+a^2f(frac{n}{b^2})+cdots+a^{log_bn-1}f(frac{n}{b^{log_bn-1}})geq f(n)),得到 (g(n)=Omega(f(n)))。
所以 (g(n)=Theta(f(n)))。
性质 3 得证。
性质 4:
老套路,将 (f(n)=Theta(n^{log_ba}log^kn)) 代入进 (g(n)=sum_{j=0}^{log_bn-1}a^jf(frac{n}{b^j})):
和性质 2 一样: (log_bn) 可以直接化为 (log n)。然后 (-n^{log_ba}sum_{j=0}^{log_bn-1}log^k b^j) 是一个负数,在时间复杂度的表示中可以忽略,所以得到:
性质 4 得证。
主定理总时间复杂度证明:
回到图二的总时间复杂度 (T(n)=Theta(n^{log_ba})+sum_{j=0}^{log_bn-1}a^jf(frac{n}{b^j}))。
当 (f(n)=O(n^{(log_ba)-epsilon})) 时:
当 (f(n)=Theta(n^{log_ba})) 时:
当 (f(n)=Omega(n^{(log_ba)+epsilon})),且对于一个常数 (c<1) 和所有足够大的 (n) 有 (af(frac{n}{b})leq cf(n)):
当 (f(n)=Theta(n^{log_ba}log^kn)) 时:
主定理证毕。
后记 & 感谢名单:
在这篇文章证明的主定理只是对于 (b) 的幂下的,就是说向上、向下取整的没有证明,但要证明这些实在太难了,要花费更长的时间 (主要还是懒。
我在查找资料的时候发现还有一种定理——Akra-Bazzi定理(打开要梯子)也是时间复杂度的。