<H2>复杂性的计量</H2><>算法的复杂性是算法运行所需要的计算机资源的量,需要的时间资源的量称作时间复杂性,需要的空间(即存储器)资源的量称作空间复杂性。这个量应该集中反映算法中所采用的方法的效率,而从运行该算法的实际计算机中抽象出来。换句话说,这个量应该是只依赖于算法要解的问题的规模、算法的输入和算法本身的函数。如果分别用<I>N、I</I>和<I>A</I>来表示算法要解问题的规模、算法的输入和算法本身,用<I>C</I>表示算法的复杂性,那么应该有:</P><><I>C </I>=<I>F</I>(<I>N,I,A</I>)</P><>其中<I>F(N,I,A)</I>是<I>N,I,A</I>的一个确定的三元函数。如果把时间复杂性和空间复杂性分开,并分别用<I>T</I>和<I>S</I>来表示,那么应该有:</P><P><I>T </I>=<I>T</I>(<I>N,I,A</I>) (2.1)</P><P>和 <I>S </I>=<I>S</I>(<I>N,I,A</I>) (2.2)</P><P>通常,我们让A隐含在复杂性函数名当中,因而将(2.1)和(2.2)分别简写为</P><P><I>T </I>=<I>T</I>(<I>N,I</I>)</P><P>和 <I>S </I>=<I>S</I>(<I>N,I</I>)</P><P>由于时间复杂性和空间复杂性概念类同,计算方法相似,且空间复杂性分析相对地简单些,所以下文将主要地讨论时间复杂性。</P><P>下面以<I>T</I>(<I>N,I</I>)为例,将复杂性函数具体化。</P><P>根据<I>T</I>(<I>N,I</I>)的概念,它应该是算法在一台抽象的计算机上运行所需的时间。设此抽象的计算机所提供的元运算有<I>k</I>种,他们分别记为<I>O</I><SUB>1</SUB><I>,O</I><SUB>2 </SUB><I>,..,O</I><SUB>k</SUB>;再设这些元运算每执行一次所需要的时间分别为<I>t</I><SUB>1</SUB><I>,t</I><SUB>2</SUB><I>,</I>..<I>,t</I><SUB>k</SUB> 。对于给定的算法<I>A</I>,设经过统计,用到元运算<I>O</I><SUB>i</SUB>的次数为<I>e</I><SUB>i</SUB><I>,</I>i<I>=1,2,..,k</I> ,很明显,对于每一个i<I>,1<=</I>i<I><=k,e</I><SUB>i</SUB>是<I>N</I>和<I>I</I>的函数,即<I>e</I><SUB>i</SUB>=<I>e</I><SUB>i</SUB>(<I>N,I</I>)。那么有:</P><P><img src="http://algorithm.myrice.com/algorithm/complexity/images/img2.gif"> (2.3)</P><P>其中<I>t</I><SUB>i</SUB>,i=1,2,..,k,是与<I>N,I</I>无关的常数。</P><P>显然,我们不可能对规模<I>N</I>的每一种合法的输入<I>I</I>都去统计<I>e</I><SUB>i</SUB>(<I>N,I</I>),i=1,2,…,k。因此<I>T</I>(<I>N,I</I>)的表达式还得进一步简化,或者说,我们只能在规模为<I>N</I>的某些或某类有代表性的合法输入中统计相应的<I>e</I><SUB>i<I> </I></SUB><I>, </I>i<I>=1,2,…,k</I>,评价时间复杂性。</P><P>下面只考虑三种情况的复杂性,即最坏情况、最好情况和平均情况下的时间复杂性,并分别记为<I>T</I><SUB>max</SUB><I>(N )、T</I><SUB>min</SUB>(<I>N</I>)和<I>T</I><SUB>avg</SUB>(<I>N </I>)。在数学上有:</P><P><img src="http://algorithm.myrice.com/algorithm/complexity/images/img5.gif"> (2.4)</P><P><img src="http://algorithm.myrice.com/algorithm/complexity/images/img7.gif"> (2.5)</P><P><img src="http://algorithm.myrice.com/algorithm/complexity/images/img9.gif"> (2.6)</P><P>其中,<I>D<SUB>N</SUB></I>是规模为<I>N</I>的合法输入的集合;<I>I</I><SUP> *</SUP>是<I>D<SUB>N</SUB></I>中一个使<I>T</I>(<I>N,I</I><SUP> *</SUP>)达到<I>T</I><SUB>max</SUB><I>(N)</I>的合法输入,<SUB><img src="http://algorithm.myrice.com/algorithm/complexity/images/img11.gif"></SUB>是<I>D<SUB>N</SUB></I>中一个使<I>T(N,</I><SUB><img src="http://algorithm.myrice.com/algorithm/complexity/images/img12.gif"></SUB>)到<I>T</I><SUB>min</SUB><I>(N)</I>的合法输入;而<I>P</I>(<I>I</I>)是在算法的应用中出现输入<I>I </I>的概率。</P><P>以上三种情况下的时间复杂性各从某一个角度来反映算法的效率,各有各的用处,也各有各的局限性。但实践表明可操作性最好的且最有实际价值的是最坏情况下的时间复杂性。下面我们将把对时间复杂性分析的主要兴趣放在这种情形上。</P><P>一般来说,最好情况和最坏情况的时间复杂性是很难计量的,原因是对于问题的任意确定的规模N达到了<I>T</I><SUB>max</SUB><I>(N)</I>的合法输入难以确定,而规模N的每一个输入的概率也难以预测或确定。我们有时也按平均情况计量时间复杂性,但那时在对<I>P(I)</I>做了一些人为的假设(比如等概率)之后才进行的。所做的假设是否符合实际总是缺乏根据。因此,在最好情况和平均情况下的时间复杂性分析还仅仅是停留在理论上。</P><P>现在以<a href="http://algorithm.myrice.com/algorithm/complexity/chapter1.htm" target="_blank" >上一章</A>提到的<a href="http://algorithm.myrice.com/algorithm/complexity/chapter1.htm#pro1" target="_blank" >问题1</A>的<a href="http://algorithm.myrice.com/algorithm/complexity/chapter1.htm#search" target="_blank" >算法Search</A>为例来说明如何利用(2.4)-(2.6)对它的<I>T</I><SUB>max</SUB><I>、T</I><SUB>min</SUB>和<I>T</I><SUB>avg</SUB>进行计量。这里问题的规模以m计算,算法重用到的元运算有赋值、测试和加法等三种,它们每执行一次所需的时间常数分别为<I>a,t</I>,和<I>s </I>。对于这个例子,如假设c在A中,那么容易直接看出最坏情况的输入出现在c<I>=A</I>[m]的情形,这时:</P><P><I>T</I><SUB>max</SUB>(<I>m</I>)=<I>a</I>+2<I>mt</I>+(<I>m</I>-1)<I>s</I>+(<I>m</I>-1)<I>a</I>+<I>t</I>+<I>a</I>=(<I>m</I>+1)<I>a</I>+(2<I>m</I>+1)<I>t</I>+(<I>m</I>-1)<I>s</I> (2.7)</P><P>而最好情况的输入出现在c=A[1]的情形。这时:</P><P><img src="http://algorithm.myrice.com/algorithm/complexity/images/img15.gif"> (2.8)</P><P>至于<I>T</I><SUB>avg</SUB>(<I>m</I>),如前所述,必须对<I>D</I><SUB>m</SUB>上的概率分布做出假设才能计量。为简单起见,我们做最简单的假设:<I>D</I><SUB>m</SUB>上的概率分布是均等的,即<I>P</I>(<I>A</I>=c)=<I>1/m </I>。若记<I>T</I><SUB>i</SUB><I>=T</I>(<I>m,I</I><SUB>i</SUB>),其中<I>I</I><SUB>i</SUB>表示<I>A</I>=c的合法输入,那么:</P><P><img src="http://algorithm.myrice.com/algorithm/complexity/images/img18.gif"> (2.9)</P><P>而根据与(2.7)类似的推导,有:</P><P><img src="http://algorithm.myrice.com/algorithm/complexity/images/img21.gif"></P><P>代入(2.9) ,则:</P><P><img src="http://algorithm.myrice.com/algorithm/complexity/images/img23.gif"></P><P>这里碰巧有:</P><P><I>T</I><SUB>avg</SUB>(<I>m</I>)=(<I>T</I><SUB>max</SUB>(<I>m</I>)+<I>T<SUB>min</SUB></I>(<I>m</I>))/2</P><P>但必须指出,上式并不具有一般性。</P><P>类似地,对于算法B_Search照样可以按(2.4)-(2.6)计算相应的<I>T</I><SUB>max</SUB>(<I>m</I>)<I>、T</I><SUB>min</SUB>(<I>m</I>)和<I>T</I><SUB>avg</SUB>(<I>m</I>)<I><SUB> </SUB></I>。不过,我们这里只计算<I>T</I><SUB>max</SUB>(<I>m</I>) 。为了与Search比较,仍假设c在A中,即最坏情况的输入仍出现在c=A[m]时。这时,while循环的循环体恰好被执行了log<I>m </I>+1 即<I>k</I>+1 次。因为第一次执行时数据的规模为m,第二次执行时规模为m/2等等,最后一次执行时规模为1。另外,与Search少有不同的是这里除了用到赋值、测试和加法三种原运算外,还用到减法和除法两种元运算。补记后两种元运算每执行一次所需时间为b和d ,则可以推演出:</P><P><img src="http://algorithm.myrice.com/algorithm/complexity/images/img25.gif"> (2.10)</P><P>比较(2.7)和(2.10) ,我们看到m充分大时,在最坏情况下B_Search的时间复杂性远小于Search的时间复杂性。</P> |