复杂度分析相关

一.复杂度的定义.

复杂度:复杂度是衡量一个代码质量的重要指标,主要分为时间复杂度空间复杂度,其中又以时间复杂度更为重要.

复杂度记号:一个算法 X X X的复杂度上界 O ( X ) O(X) O(X)定义为 X X X的运行总量多项式只保留最高次项并去掉常数后的值,例如:
O ( 3 n 2 + 5 n + 7 ) = O ( n 2 ) O ( 7 8 n + 10 log ⁡ 3.4 n ) = O ( n ) O ( 5 n 2 log ⁡ 3.5 3 ( 100 n ) ) = O ( n 2 log ⁡ 3 n ) O ( 2 n + 2 + 8 n 7 ) = O ( 2 n ) O(3n^{2}+5n+7)=O(n^{2})\\ O(7\sqrt{8n}+10\log_{3.4} n)=O(\sqrt{n})\\ O(5n^{2}\log_{3.5}^{3}(100n))=O(n^{2}\log^{3} n)\\ O(2^{n+2}+8n^{7})=O(2^{n}) O(3n2+5n+7)=O(n2)O(78n +10log3.4n)=O(n )O(5n2log3.53(100n))=O(n2log3n)O(2n+2+8n7)=O(2n)

当然有些时候常数对代码的效率影响也很大,但在复杂度计算中不予考虑.

其实记号 O O O表示的只是一个上界,也就是说一个算法的复杂度为 O ( n 2 ) O(n^2) O(n2),那么它的复杂度也可以被写为 O ( n 3 ) O(n^3) O(n3).

再来一些实例解释一下吧,比如说下面这串代码:

int ans=0;
for (int i=1;i<=n;i+=5)
  for (int j=n;j>=10;--j)
    for (int k=1;k<=n;k+=2) ++ans;

这串代码的时间复杂度就是 O ( n 3 ) O(n^3) O(n3).

在比如说下面这串代码:

int ans=0;
for (int i=1;i<=n;i<<=1)
  for (int j=2;j<1<<i;++j)
    for (int k=4;k*k<=n;++k) ++ans;

这串代码的时间复杂度就是 O ( 2 n n log ⁡ n ) O(2^{n}\sqrt{n}\log n) O(2nn logn).

除此之外,复杂度还有记号例如 Ω ( n ) \Omega(n) Ω(n) Θ ( n ) \Theta(n) Θ(n),其中 Ω \Omega Ω表示下界, Θ \Theta Θ表示上下都有界.


二.积分估计计算时间复杂度.

若对于某一个和式:
∑ i = 1 n f ( i ) \sum_{i=1}^{n}f(i) i=1nf(i)

其中 f ( x ) f(x) f(x)是一个多项式,但这个东西并不好估计它的具体值,我们该如何计算呢?

有一个非常简单的方法,直接将这个和式看成积分的离散表示,然后直接用积分算复杂度.

由于复杂度计算的只是大概值,所以这样算是没有关系的.

也就是说:
O ( ∑ i = 1 n f ( i ) ) = O ( ∫ 1 n f ( i ) d i ) O\left(\sum_{i=1}^{n}f(i)\right)=O\left(\int_{1}^{n}f(i)\mathrm{d}i\right) O(i=1nf(i))=O(1nf(i)di)

一个经典的例子是计算如下和式在复杂度表示中的值:
O ( n ∑ i = 1 n 1 i ) = O ( n ∫ i = 1 n 1 i d i ) = O ( n log ⁡ n ) O\left(n\sum_{i=1}^{n}\frac{1}{i}\right)=O\left(n\int_{i=1}^{n}\frac{1}{i}\mathrm{d}i\right)=O(n\log n) O(ni=1ni1)=O(ni=1ni1di)=O(nlogn)



三.主定理.

对于一个递归函数:
T ( n ) = a T ( n b ) + f ( n ) T(n)=aT(\frac{n}{b})+f(n) T(n)=aT(bn)+f(n)

如何计算 O ( T ( n ) ) O(T(n)) O(T(n))呢?

主定理:对于 T ( n ) = a T ( n b ) + f ( n ) T(n)=aT(\frac{n}{b})+f(n) T(n)=aT(bn)+f(n),则有:
O ( T ( n ) ) = { O ( n log ⁡ b a ) ∃ k > 0 ⇒ f ( n ) = O ( n log ⁡ b a − k ) O ( n log ⁡ b a log ⁡ k + 1 n ) ∃ k > 0 ⇒ f ( n ) = O ( n log ⁡ b a log ⁡ k n ) O ( f ( n ) ) ∃ k > 0 ⇒ f ( n ) = Ω ( n log ⁡ b a + k ) O(T(n))= \left\{\begin{matrix} O(n^{\log_{b}a})&\exists k>0\Rightarrow f(n)=O(n^{\log_{b}a-k})\\ O(n^{\log_{b}a}\log^{k+1}n)&\exists k>0\Rightarrow f(n)=O(n^{\log_{b}a}\log^{k}n)\\ O(f(n))&\exists k>0\Rightarrow f(n)=\Omega(n^{\log_{b}a+k}) \end{matrix}\right. O(T(n))=O(nlogba)O(nlogbalogk+1n)O(f(n))k>0f(n)=O(nlogbak)k>0f(n)=O(nlogbalogkn)k>0f(n)=Ω(nlogba+k)

有了主定理我们就可以算很多分治算法的复杂度了,例如:
T ( n ) = 2 T ( n 2 ) + O ( 1 ) ⇒ O ( T ( n ) ) = O ( n ) T ( n ) = T ( n 2 ) + O ( 1 ) ⇒ O ( T ( n ) ) = O ( log ⁡ n ) T ( n ) = 2 T ( n 2 ) + O ( n log ⁡ n ) ⇒ O ( T ( n ) ) = O ( n log ⁡ 2 n ) T ( n ) = 2 T ( n 2 ) + O ( n 2 ) ⇒ O ( T ( n ) ) = O ( n 2 ) T(n)=2T(\frac{n}{2})+O(1)\Rightarrow O(T(n))=O(n)\\ T(n)=T(\frac{n}{2})+O(1)\Rightarrow O(T(n))=O(\log n)\\ T(n)=2T(\frac{n}{2})+O(n\log n)\Rightarrow O(T(n))=O(n\log^2 n)\\ T(n)=2T(\frac{n}{2})+O(n^2)\Rightarrow O(T(n))=O(n^2) T(n)=2T(2n)+O(1)O(T(n))=O(n)T(n)=T(2n)+O(1)O(T(n))=O(logn)T(n)=2T(2n)+O(nlogn)O(T(n))=O(nlog2n)T(n)=2T(2n)+O(n2)O(T(n))=O(n2)


四.均摊分析.

有些时候,一个算法有 n n n步,处理每一步的时候复杂度可能达到 O ( n ) O(n) O(n),但其总复杂度却可以保证 O ( n ) O(n) O(n),是不是非常神奇?

是的,确实有这种算法,而且还有不少,例如KMP就是其中最经典的一种.

那么它的复杂度是怎么保证的呢?有一个叫均摊复杂度方法可以证明.

例如KMP的时间复杂度分析过程:我们注意到KMP的每执行一次 j = n e x t [ j ] j=next[j] j=next[j]都会使得 j j j至少 − 1 -1 1,而总共 n n n步中 j j j每步最多只能 + 1 +1 +1,且 j j j无论何时都是 > 0 >0 >0的,所以它的复杂度是 O ( n ) O(n) O(n)的.

这就是一种均摊时间复杂度的方法.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值