递推式分析

                                                                                      《目录》


    算法设计的时候,可能会设计出不错的算法;难点在于算法分析。

    对于更好的算法设计来说,先从分析着手,在这个基础上去寻找看看能不能找到合适算法设计的思路。

    我们看看 分治算法 :

  •     分
  •     治
  •     合

 

    好算法要设计出来,每个步骤都得细细的琢磨琢磨:

  •    分,思考 <如何分>
  •    治,思考 <巧妙治>
  •    合,思考 <快速合>

    可哪怕,我们真的按照上面的原则设计出一个分治算法,也不知道该算法在渐近意义上的时空复杂度是多少!

    分治算法的分析 ,需要借助于数学?。

 

    工具一,就是递归,没错,其实递归程序设计的灵感是源于数学的。

    因为递归和分治相似,递归的表达又十分简洁,用作估计是再好不过了。

    递归的俩种情形:

  •   基础情形
  •   递归情形

 

    分治算法的分析模板

            分治算法运行时间的递归式来自基本的三个步骤(分、治、合),假设分治算法运行的时间是 T(n)

            如果问题规模 n 足够小,有一个常量 c,n\leqslant c,则渐进意义上的复杂度是 \Theta(1)

            一个问题被分解为 a 个子问题,每个子问题的规模是原问题的 \frac{1}{b} 。

            举个例子,使用分治的归并排序:

            对归并排序,a 和 b 都是 2,不是嘛。其实,大部分的分治算法,a\neq b

            为了求解()一个规模为 \frac{n}{b} 的子问题,需要 T(\frac{n}{b}) 的时间来求解 a 个子问题。

            如果解问题 成子问题需要的时间为 D(n)并子问题的解 成原问题的解需要的时间为 C(n),那么可得到递归式:

             T(n)=\left\{\begin{matrix}\Theta(1) & n\leqslant c \\ a*T(\frac{n}{b})+D(n)+C(n) & other\end{matrix}\right. 

             求解分治算法的复杂度,往上面套就好了,重在理解。

 

             如果您理解了,分析归并排序的时间复杂度,应该是过家家。

              T(n)=\left\{\begin{matrix}\Theta(1) & n=1 \\ 2*T(\frac{n}{2})+D(n)+C(n) & n>1\end{matrix}\right.

              再逐步细分,当 n > 1 个元素时,我们分解运行时间:

  •               分:只是计算了子数组的中间位置,D(n) 属于常量时间 ,因此,D(n) =\Theta (1)
  •               治:因为 a = b =2,归并递归的求解俩个规模为 \frac{n}{2} 的子问题,渐进意义上的时间复杂度为 2*T(\frac{n}{2}) 。
  •               合:一个具有 n  个元素的子数组在合并的过程中需要 \Theta (n) 的时间,因此,C(n) =\Theta (n)

              因为 \Theta (1) + \Theta (n) =\Theta (n),因此我们可以省略 D(n) 和 C(n) 改写为 \Theta (n)

               T(n)=\left\{\begin{matrix} \Theta (1) & n=1 \\ 2*T(\frac{n}{2})+\Theta (n) & n>1\end{matrix}\right.

 

递推式

              一般我们做递推式的考察时,有三种常用方法:

  •           代换法:通过猜想的方式,先找到一个合适的猜想,再用数学归纳法去证明 TA;
  •           递归树:递归树的结点可以做为时间开销,有点像分摊复杂性,把一些时间分摊到不同的位置上,最后求和即可;
  •           主定理:无脑推导 (只需要往下推就行,不需要看什么形态),T(n) = a*T(\frac{n}{b})+f(n);

              学习算法的同僚们,一般不会直接学习主定理,而是先掌握 代换法 的基本思想,知道怎么证明这个问题;之后再学递归树来猜测,在递归树的猜测和分析之中,主定理的形态就呼之欲出了。

              另外,一般我们会考虑 不等式 的记号,出现 不等式 的递推式,我们该如何处理呢 ??

 

              如不等式,T(n) \leqslant 2*T(\frac{n}{2})+\Theta (n) 

              小于等于,用至多表示,至多也就是 O 记号:

              假设,我们会用到另一个量 T'(n),且 T(n) \leqslant T'(n)得到T'(n) = 2*T'(\frac{n}{2})+\Theta (n)

              做这一步,最主要的是为了将 \leqslant 转为 = ,T'(n) 得到什么样的形态,T(n) 只需要用 大O记号 就可以知道了。

              构造递归树(算法导论 P21 有记录,因为比较难所以记录在代换法之下),得到 T'(n)=\Theta (n*logn), T(n)\leqslant T'(n),    T(n)=O(n*logn)

 

 

代换法求解递推式

              又如不等式,T(n) \geqslant 2*T(\left \lfloor \frac{n}{2} \right \rfloor)+\Theta (n),有一个向下取整 \left \lfloor \right \rfloor 是因为,当 n 为奇数时,\frac{n}{2} 不是整数。

              大于等于,用至少表示,至少也就是 \Omega 记号:

              代换法的思想:先猜后证,代换法中难点在于 猜想,能够给出一个好的猜想,这样的家伙肯定是 真正理解 了,能举一反三。

              假设我们已经知道 T(n) = 2T(\left \lfloor \frac{n}{2} \right \rfloor)+n,那我们该如何求解 TA ??

              先猜后证。

              我们猜 T(n)\leqslant c*nlogn(凭感觉猜) , 小于等于,用至多表示,至多也就是 O 记号,T(n) = O(nlogn)

  •               具体的猜法:每次取一半(二分),取到 1 时 需要 logn 次,每次有 n 的时间开销,显然这个算法可以是组合估计 。

              即 n*logn ,我们就猜 nlogn。   

 

              第二步,证明即可。

              假设 m<n 时都成立,我们要证明  T(\left \lfloor \frac{n}{2} \right \rfloor)\leqslant c\left \lfloor \frac{n}{2} \right \rfloor log \left \lfloor \frac{n}{2} \right \rfloor 。

               T(n) = 2T(\left \lfloor \frac{n}{2} \right \rfloor)+n

               T(n)\leqslant 2c* \frac{n}{2} log \frac{n}{2} +n 

                         = cn(logn-1)+n

                         = cn*logn+(1-c)n                       

               加强假设,要求 (1-c)< 0,但负的系数对其没有什么影响,因此我们得假设 c > 1

                          \leqslant cn*logn       得证。 

               

               猜想是一个技术活,猜不出来就真的证明不出来;不过也有一些地方,可以套:

                如,       T(n) = 2T(\left \lfloor \frac{n}{2} \right \rfloor)+n     与      T(n) = 2T(\left \lfloor \frac{n}{2} \right \rfloor+42)+n

                这时的猜想 T(n) 即可,因为 T(n) , T(n+\Delta ) 相差不大,注意:在渐近意义上是等价的,但证明的时候不要用渐进记号 (因为误差会逐步增大,最多只能用在最后一次,数学证明一定要严谨)。

                有加法,证明会难一些。

                 T(n)\leqslant 2c*(\frac{n}{2}+42)*log(\frac{n}{2}+42)+n       

                 在对数里有加法,这个比较困难,因此理想的方法是把加法划掉。

                 我们可以假设,\frac{n}{2}+42< \frac{2}{3}n ~~(n>?) ,怎么得到的呢 ?

                 不等式左边的 n 需要除以 2,不等式右边的 n 除以 1.5,除以 2 的自然会比除以 1.5 的小,这样就划掉加法了,不过 n 要比较大时,这个不等式才成立,具体的数是 n > 252 时,不等式才成立,不要问我为什么,解不等式就能得出来。

                  T(n)\leqslant 2c*(\frac{n}{2}+42)*log(\frac{2}{3}n)+n,只需要对调对数部分即可。

                            =(cn+84c)*log(\frac{2}{3}n)+n

                            =cn*log(\frac{2}{3}n)+84c*log(\frac{2}{3}n)+n       

                            =cn*logn - cn*log\frac{3}{2}+84c*log(\frac{2}{3}n)+n  ,log(\frac{2}{3}) 提出来后,再颠倒变化。

                  将 -cn*log\frac{3}{2}+84c*log(\frac{2}{3}n)+n 变成负数项。

                  对比三项:-cn*log\frac{3}{2} 、84c*log(\frac{2}{3}n) 、 n,显然 -cn*log\frac{3}{2} 最大,只要让 常数 c 大一点,就可以通过 第一项 吃掉 第二、三项。

                             \leqslant cn*logn    ,最后的证明结果,细节比较繁琐。

 

 

更强的归纳假设                           

              在归纳证明时,我们可以需要一个比较强的归纳。

              假设我们要证明 T(n)\leqslant f(n),直接证明比较困难。

              可以改为 T(n)\leqslant f(n)-g(n),从渐进记号的意义上 g(n) 比 f(n) 弱。

            《算法导论》给出了一个例子:

               T(n)=T(\left \lfloor \frac{n}{2} \right \rfloor)+T(\left \lceil \frac{n}{2} \right \rceil)+1     ,去掉了渐进记号,方便证明。                 

               这个式子有一个麻烦的地方,式子最后的 +~1

               假设 T(n)\leqslant cn,那么

                T(n)\leqslant c\left \lfloor \frac{n}{2} \right \rfloor+c\left \lceil \frac{n}{2} \right \rceil+1 

                          =cn+1 ,化简   

                T(n)=cn+1 和我们之前的假设 T(n)\leqslant cn 不一样,不符号。

               这时候,就该使用更强的归纳假设!!!

               上面差了一个常数项 1,我们要消掉。

               我们可假设 T(n)\leqslant cn-dd 是一个差项,上面差项是 1,d=1

                T(n)\leqslant c\left \lfloor \frac{n}{2} \right \rfloor-d+c\left \lceil \frac{n}{2} \right \rceil-d+1

                          =cn-2d+1 ~(\leqslant cn-d~)

                          d\geqslant 1,才成立。

               数学归纳法常用的技巧,更强的归纳假设就推出更多的信息,证明就会方便一些。

                       

              

                                     

递归树求解递推式

               递归树每个结点的代价,是函数调用的开销呀,包括 “分”、“合”。

               假设我们的结点调用 m 参数发生的,这部分开销是 f(m)

               我感觉《算法导论》上写的特别棒,这部分我实在是不想自己再写,佛了《算法导论》的递归树,那个简约又实用,还简单。

               不过,我还是打算写一遍,表达式会复杂一些,不过推理过程很完整。

                 T(n)=3*T(\left \lfloor \frac{n}{4} \right \rfloor)+\Theta (n^{2})

               我们分析算法,可以去掉细节,比如 向下取整、渐进记号。

                 T(n)=3*T(\frac{n}{4})+cn^{2}

               假设 n 恰好是 4 的幂 n = 4^{L},对比一下算法导论,就发现这一步是假设 n 恰好是 b 的幂。

               以幂的形式来给出简单的假设,我们要证明的是 T(n)=3*T(\frac{n}{4})+cn^{2}

               使用递归树证明时,把递归树一步一步画出来。

               画出 T(n)

                选择 cn^{2} 代 T(n),因为 cn^{2} 是多项式中的最大项:                

 

               

                第 1 层,所有结点相加结果是 cn^{2} ;

                第 2 层,所有结点相加结果是 \frac{3}{16}cn^{2}

                第 3 层,所有结点相加结果是 (\frac{3}{16})^{2}cn^{2}

                结果分为俩部分了,后面的一直都是 cn^{2},前面的是 \frac{3}{16} 的等比数列。

                算到最后一层,全部是 T(1)

                请问,有多少个 T(1) 呢 ???

                首先要搞清楚,从上至下共多少层!!!

                在开始的地方,我们假设了 n = 4^{L},也就是 L 层。

                请问,整个递归树有多少个叶子结点 ??

                叶子结点的个数以 3 为基础变化,3^{L} 个叶子结点(第一层是 3 个,第二层是 9 个,第三层是 27 个)。

                因为 L = log_{4}n,所以 3^{L} = 3^{log_{4}n}

                3 和 n 可以交换,因为这样会比较好用变成了 n 次方,即 3^{L} = n^{log_{4}3}

                总时间的开销:

                 T(n)=cn^{2}+\frac{3}{16}cn^{2}+(\frac{3}{16})^{2}cn^{2}+\cdots +(\frac{3}{16})^{L-1}cn^{2}+\Theta (n^{log_{4}3})

                 因为每一项都有规律,前面的系数是:

                     1+\frac{3}{16}+(\frac{3}{16})^{2}+\cdots +(\frac{3}{16})^{L-1}\leqslant \frac{1}{1-\frac{3}{16}}  ,小于一个无穷序列。

                                                                             =\frac{16}{13}

                  因此,T(n)< \frac{16}{13}cn^{2}+\Theta (n^{log_{4}3})

                                       =O (n^{2})+\Theta (n^{log_{4}3})

                                       =O(n^{2}) ,大鱼吃小鱼。

               

                  每一个结点,对应的是调用 m 的 f(m)  ,f(m)=cm^{2}

                  在递归树中逐层分解,直到叶子结点都是 O(1) 时结束。             

 

 

 

 

 

 

 

 

 

               

               

 

 

 

 

 

 

 

   

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值