大O符号
1. 定义
大O符号(Big O notation)是用于描述函数渐进行为的数学符号。
也可以这么说:
用一个大O,在其括号()中,用另一个函数来描述原来的函数的数量级的渐进上界
计算机科学中,用于分析算法复杂性非常有用
2. 使用
这个符号有两种形式上很接近但迥然不同的使用方法:无穷大渐近与无穷小渐近。然而这个区别只是在运用中的而不是原则上的——除了对函数自变量的一些不同的限定, “大O”的形式定义在两种情况下都是相同的
分析算法复杂度时,用的O都代表无穷大渐进,表示n趋近于无穷大的情况
2.1 无穷大渐进
举例如下:
假设,解决一个规模为n的问题所花费的时间(或者所需步骤)可以表示为:
T(n) = 4n2 - 2n + 2
当n越来越增大时,n2项将开始占主导地位,而其它各项可以被忽略(自然包括2)
例如:当n=500时,4n2 项是2n项的1000倍大,因此在大多数非精确场合下,省略2n项对表达式值得影响是可以忽略不计的
其实进一步看,与n2的指数相比其系数4(与指数增大相比系数是很小的)也是无关紧要的
这样,大O符号就记下剩余的部分,写作:
T(n)∈O(n2)
或者更常见的
T(n)=O(n2)
此时,我们就说该算法具有n2阶(平方阶)的时间复杂度
2.2 无穷小渐进
暂不描述
3. 常用函数阶
下表是在分析算法时常见的函数。这些函数都处于n趋近于无穷大的情况下,在这种情况下函数结果值增长的慢的在表上方。c是一个任意常数,重点是n
符号 | 名称(后面都要+个“阶”) |
---|---|
O(1) | 常数 |
O(log n) | 对数,情况最多的底数为2(但也可能为其它),但是底数无关紧要,所以不明确说底数 |
O[(long n)c] | 多对数 |
O(n) | 线性 |
O(n log* n) | log* n为迭代对数 |
O(n log n) | 线性对数 |
O(n2) | 平方 |
O(nc),Interger(c>1) | 多项式,有时叫“代数” |
O(cn) | 指数,有时叫“几何” |
O(n!) | 阶乘,有时叫“组合” |
时间复杂度
同一个算法用不同的语言实现,或者用不同的编译程序进行编译,或者在不同的计算机上运行时,效率都肯定不同。这表明使用绝对的时间单位衡量算法的效率是不合适的。撇开这些与计算机软硬件有关的因素,可以认为一个特定的算法“运行工作量”的大小,只依赖于问题的规模(通常用n来表示),或者说它是问题规模n的函数。
算法由 控制结构(顺序、分支和循环)+原操作(数据类型固有的操作) 构成,则算法时间取决于两者的综合效果。通常会选取一种对于所研究的问题来说是基本操作的原操作,以该原操作重复执行的次数作为算法的时间量度。
一般情况下,算法中的基本操作重复执行次数是问题规模n
的某个函数f(n)
,算法的时间量度记作:
T(n)=O(f(n))
称作算法的渐近时间复杂度(asymptotic time complexity),简称为时间复杂度。
通常情况下:原操作就是程序中最深层循环内的语句中的原操作;通常在一个算法内只需要选择一种基本操作来讨论即可,但不排除有时候需要同时考虑几种基本操作。
由于算法的时间复杂度考虑的只是对于问题规模n的增长率,则在难以精确计算基本操作执行次数的情况下,只需求出它关于n的增长率或阶即可。
有的情况下,算法中基本操作重复执行的次数还随着问题的输入数据集不同而不同,例如冒泡算法。对这类问题的分析:
- 一种办法是计算平均时间复杂度,不过由于各种输入集出现的概率难以确定,算法的平均时间复杂度也很难确定;
- 更可行也更常用的方式是,讨论算法在最坏情况下的时间复杂度,通常讨论时间复杂度,如果没有特别说明则均指的是最坏时间复杂度;
- 最好时间复杂度,一般不讨论,没有意义。