算法复杂性

我们如何定义一个程序的运行时间?为了排除“不同计算机”的运行速度对时间的影响,我们将一个程序的运行时间定义为“一个固定计算模型上单位操作的次数”,这个固定的计算模型一般指图灵机。

程序的运行时间与输入规模有关,也与程序采用的算法有关。同样规模的输入,由于其数据可能具有不同的“性质”,也会导致程序运行的时间有所不同。因此,我们用时间复杂度的概念来描述程序对于输入规模(比如\(n\))在最坏情况下的运行时间。

我们关心复杂度的级别而不是复杂度的细节,因此我们引入高阶无穷大中的记号:

如果\(T(n),g(n)\)\(n \to \infty\)时有\(\dfrac{T(n)}{g(n)} \leq C\)(有上界),就记\(T(n)=O(g(n))\)。其中\(g(n)\)是我们的一个标尺,对于多项式可以取\(n,n^2\cdots\)。如果取\(g(n)=n^2\),那么形如\(T(n)=n^2,2n^2,5n^2+7n+9,n,2n+3,1\)的复杂度都可以被记为\(T(n)=O(n^2)\),意思是\(T(n)\)不会超过\(g(n)\)这个级别,\(T(n)=n^3\)就不满足\(T(n)=O(n^2)\)。总之,\(O(g(n)) \leq C \cdot g(n)\)

如果\(T(n),g(n)\)\(n \to \infty\)时有\(\dfrac{T(n)}{g(n)} \geq C\)(有下界),就记\(T(n)=\Omega(g(n))\)。如果取\(g(n)=n^2\),那么形如\(T(n)=n^2,2n^2,5n^2+7n+9,n^3,2^n\)的复杂度都可以被记为\(T(n)=\Omega(n^2)\),意思是\(T(n)\)至少有\(g(n)\)这个级别,\(T(n)=n,1\)等就不满足\(T(n)=\Omega(n^2)\)。总之,\(\Omega(g(n)) \geq C \cdot g(n)\)

如果又有\(T(n)=O(g(n)),T(n)=\Omega(g(n))\),那么就记\(T(n)=\Theta(g(n))\)。如果取\(g(n)=n^2\),那么形如\(T(n)=n^2,2n^2,5n^2+7n+9\)的复杂度都可以被记为\(T(n)=\Theta(n^2)\),意思是\(T(n)\)正好就是\(g(n)\)这个级别,\(T(n)=n,n^3\)等都是不满足\(T(n)=\Theta(n^2)\)的。\(\Theta\)是一种精确的描述。总之,\(C_1 \cdot g(n) \leq \Theta(g(n)) \leq C_2 \cdot g(n)\)

举个例子来分析算法的时间复杂度:求\(n\)个数中的第\(k\)小。一种思路是,每次找最小然后删除,复杂度\(O(n^2)\);一种思路是,先排序然后取第\(k\)个,复杂度\(O(n\log n)\)。有一个称为“中位数的中位数”的算法,首先把整个数组五个五个分组,求出每组的中位数,复杂度\(O(n)\);再求出这些中位数的中位数\(v\),这是一个递归的问题,假设整个问题的时间复杂度为\(T(n)\),那么这一步耗费\(T(\dfrac{n}{5})\);把所有小于\(v\)的数放进集合\(L\),所有大于\(v\)的数放进集合\(R\),等于\(v\)的放进集合\(M\),耗费\(O(n)\);此时我们发现,在这\(\dfrac{n}{5}\)个中位数中,比\(v\)小的那些中位数所在的每个组里至少有三个比\(v\)小,比它大的也是如此。也就是说,至少有\(\dfrac{n}{2} \cdot \dfrac{3}{5}\)个数比\(v\)小,有\(\dfrac{n}{2} \cdot \dfrac{3}{5}\)个数比\(v\)大。因此有\(\dfrac{3}{10}n \leq |L|,|R| \leq \dfrac{7}{10}n\)。因此递归到\(L,R\)的复杂度不超过\(T(\dfrac{7}{10}n)\)。综上,算法的复杂度可以写成递归表达式:\(T(n)=T(\dfrac{n}{5})+T(\dfrac{7n}{10})+O(n)\)。我们把\(O(n)\)写作\(Cn\)的形式:\(T(n)=T(0.2n)+T(0.7n)+Cn\),递归地展开,最终\(T\)的项会趋向0,而带有\(Cn\)的项形成了等比数列\(T(n)=Cn+0.9Cn+0.9^2Cn+\cdots\),因此\(T(n) \leq 10Cn\),得到了\(T(n)=O(n)\)

posted @ 2022-11-06 00:06  DennyQi  阅读(58)  评论(0编辑  收藏  举报