为什么需要复杂度分析
当我们评估一个算法的执行效率的是,有一种很直观的方法叫事后统计法
,就是通过各种检测,得出某个算法实际的运行时间,但是这种方法有很多局限性:
- 测试结果非常依赖测试环境
- 测试结果受数据规模影响很大
所以我们需要一个不用具体的数据来测试,就可以粗略估计算法的执行效率的方法
四个定义:
- 如果存在正常数 c {c} c 和 n 0 {n_0} n0,使得当 N ≥ n 0 {N\geq n_0} N≥n0时, T ( N ) ≤ c f ( N ) {T(N) \le cf(N)} T(N)≤cf(N),则记为 T ( N ) = O ( f ( n ) ) {T(N) = O(f(n))} T(N)=O(f(n))(上界)
- 如果存在正常数 c {c} c 和 n 0 {n_0} n0,使得当 N ≥ n 0 {N\geq n_0} N≥n0时, T ( N ) ≥ c f ( N ) {T(N) \geq cf(N)} T(N)≥cf(N),则记为 T ( N ) = Ω ( f ( n ) ) {T(N) = \Omega(f(n))} T(N)=Ω(f(n))(下界)
- T ( N ) = Θ ( h ( N ) ) {T(N)=\Theta(h(N))} T(N)=Θ(h(N))当且仅当 T ( N ) = O ( h ( N ) ) {T(N) = O(h(N))} T(N)=O(h(N))和 T ( N ) = Ω ( h ( N ) ) {T(N) = \Omega(h(N))} T(N)=Ω(h(N))(紧确界)
- 如果对每一正常数 c {c} c都存在常数 n 0 {n_0} n0,使得当 N > n 0 {N>n_0} N>n0时, T ( N ) < c p ( N ) {T(N)<cp(N)} T(N)<cp(N),则 T ( N ) = o ( p ( N ) ) {T(N) = o(p(N))} T(N)=o(p(N))
给定的两个函数,通常会存在一些点,在这些点上一个函数的值小于另一个函数,所以单纯的比较两个函数的大小是没有意义的,我们比较的是他们的相对增长率,在描述的时候,应该说一个算法复杂度是“大O…级的”
重要结论
- 如果
T
1
(
N
)
=
O
(
f
(
N
)
)
{T_1(N) = O(f(N))}
T1(N)=O(f(N))且
T
2
(
N
)
=
O
(
g
(
N
)
)
{T_2(N) = O(g(N))}
T2(N)=O(g(N)),那么
- T 1 ( N ) + T 2 ( N ) = O ( f ( N ) + g ( N ) ) {T_1(N)+T_2(N) = O(f(N)+g(N))} T1(N)+T2(N)=O(f(N)+g(N))(直观和非直观的可以写成 m a x ( O ( f ( N ) , O ( g ( N ) ) ) {max(O(f(N),O(g(N)))} max(O(f(N),O(g(N))))
- T 1 ( N ) ∗ T 2 ( N ) = O ( f ( N ) ∗ g ( N ) ) {T_1(N)*T_2(N) = O(f(N)*g(N))} T1(N)∗T2(N)=O(f(N)∗g(N))
- 如果 T ( N ) {T(N)} T(N)是一个 k {k} k次多项式,则 T ( N ) = Θ ( N k ) {T(N) = \Theta(N^k)} T(N)=Θ(Nk)
- 对于任意常数 k {k} k, l o g k N = O ( N ) {log^kN = O(N)} logkN=O(N),它告诉我们对数增长的非常缓慢
注意不要写成 f ( N ) ≤ O ( g ( N ) ) {f(N)\le O(g(N))} f(N)≤O(g(N)),因为大 O {O} O表示法已经包含了小于等于的意思,应该直接用 f ( N ) = O ( g ( N ) ) {f(N)=O(g(N))} f(N)=O(g(N))
模型
在我们的计算模型中:
- 机器指令顺序执行
- 有一个标准的简单指令系统,如加减乘除赋值等
- 每件简单的工作都恰好花费一个一个时间单位
- 不存在矩阵求逆、排序等操作
- 无限内存