最好最坏和平均情况下的性能分析
现在有一个问题,对于所有的输入来说,前面得到的结果是否都成立呢?第二种排序法在少量字符串的时候性能也许是最好的。但是,输入数据有很多地方可能变化:
输入数据可能有1 000 000个字符串。算法如何处理如此大规模的数据?
输入数据可能会是部分有序状态,也就是说,几乎所有的元素所在的位置离最终位置并不是很远。
输入数据可能包含重复的值。
无论输入数据的规模n是多少,输入数据都可以从一个小得多的数据集扩展而来,不过会有相当多的重复值。
虽然从图2-2上来看,第四种排序法在排序n个乱序字符串的时候是最慢的,但是处理已经排序的数据却是最快的。但是,随着n的增长,第四种排序法的领先优势消失得非常快,我们从图2-3上可以看到,在对16个乱序的字符串进行排序时,第四种排序法就已经失去领头羊的位置了。
![]() |
![]() |
尽管如此,假设一个包含n个字符串的输入,并且这个输入已经是几乎有序,其中n/4个字符串(占总字符串的25%)被交换到仅仅是离最终位置4个元素。最终结果如图2-4所示,我们看到第四种排序法性能表现远远好于其他的排序算法。
![]() |
我们得到这样一个结论,对于许多问题来说,并不存在单一的最优算法。选择一个算法需要对问题有着充分的理解,并知道这个问题将要处理数据规模的概率分布情况,还有算法的实际行为也必须要考虑到。
在这里我们提供一些指导,算法通常会分成三种情况进行分析:
最坏情况
定义一类输入,在这类输入下,算法表现出了最坏的运行性能。算法设计人员需要明白,这类输入的共同性质阻止了算法高效地运行,而不只是针对特定的输入。
平均情况
平均情况是表示算法在随机给定的数据上期望的执行情况。通俗地说,一些输入可能会在某些特殊情况下耗费程序大量的时间,但是大部分的输入并不会这样。这个衡量标准描述了用户对算法性能的期望。
最好情况
定义一类输入,算法在这类输入下表现出了最好的运行性能。对于这类输入来说,算法只进行很少的计算。不过在实际情况下,最好情况很少出现。
通过分析三种情况,大致了解了算法的性能,那么接下来你需要为将要面临的问题选择一个算法。
最坏情况
大多数问题都可能会处理一些比n大的数据。任意给定一个n,算法或者程序在处理所有规模为n的数据,其性能可能会动态地发生变化。给定一个程序和n,这个程序的最坏运行时间就是处理所有规模为n的数据所需要的最长运行时间。
我们也看到,最坏情况是对整个世界的一个悲观的分析。但是我们还是非常关注最坏情况,因为:
追根刨底的欲望
这通常是对一个算法复杂度的最早的分析。
实际运行时的限制
如果你需要设计一个系统,协助外科医生进行体外循环心脏手术,那么长时间的运行(即使这种情况是不经常发生)当然不可能接受。
更正式地说,如果Sn是数据集合si中规模为n的子集,t表示算法在每一份数据上的运行时间,那么最坏情况下,对于所有si∈Sn,算法在Sn上的运行时间是t(si) 的最大值。我们将在Sn下的最长时间记做Twc(n),Twc(n) 的增长率表示算法在最坏情况下的复杂度。
一般来说,没有足够的资源来计算每一份数据si,然后检查算法在哪份数据上表现最坏。于是,给定算法的描述,算法分析人员总是想方设法地精心准备可能会导致最坏情况的输入数据。
平均情况
现在要设计一个支持n个电话的电话系统,n是一个非常大的数目,要求在最坏情况下,即n/2位用户同时呼叫另外n/2位用户时,这个系统也能够正确地处理数据。虽然这个系统不会由于过载而崩溃,但需要花费过高的代价构造这样的情况。而且,n/2位用户同时呼叫另外n/2位用户发生的概率极小。也许可以设计一个花费较小的系统在过载的情况下很少会(或者从不)崩溃。但是我们还是必需借助数学工具来计算这个概率问题。
对于一个n个样本的数据集合,我们用一个概率分布Pr表示样本的出现概率,单个样本的出现概率为0到1,所有样本的概率的和为1。如果Sn是n个样本的数据集合,那么:
![]() |
如果t表示算法在单个样本的执行时间,那么在Sn上的平均运行时间是:
![]() |
也就是说,在样本si的实际执行时间,t(si) 将会与概率加权。如果Pr{si}=0,那么t(si) 的实际值将不会影响程序的期望运行时间。我们用Tac(n) 表示算法在Sn上的平均运行时间,那么Tac(n) 的增长率表示算法在平均情况下的复杂度。
回忆一下,当我们描述时间或者工作量的增长率时,我们一直忽视了常数,所以我们认为顺序搜索n个元素的平均情况为:
![]() |
探测(符合我们之前的假设),按照惯例,在符合这些假设的前提下,期望顺序搜索能够处理线性或者是n阶的元素。
最好情况
知道算法的最好情况是非常有用的,即便这种情况很少发生。在很多情况下,最好情况能让我们看到算法的最优状况。例如,线性搜索的最好情况是当它在n个元素中搜索v的时候,第一个元素恰好就是要找的那个。一个稍微有些不同的算法,我们叫做计数搜索(Counting Search),在n个元素中搜索v,并且记录v在表中出现的次数。如果v的计数是0,那么这个值是不存在的,所以会返回false,否则返回 true。注意,计数搜索总是会搜索整个表,因此,它的最坏情况是O(n)(和顺序搜索一样),最好情况还是O(n),所以我们不能够使用这个算法,因为它的最好或者平均情况没有改善性能。