利用主定理来计算程序的时间复杂度

第二版前言

这篇文章在 2022/10/72022/10/7 重新进行了一些排版上的调整,最开始的原因是笔者认为这篇文章写的还算不错,而且里面涉及的知识以后可能还会用上,所以又传了一遍。

前言

Master\text{Master} 定理,又称主定理,用于程序的时间复杂度计算,核心思想是分治,近几年 Noip\text{Noip} 常考时间复杂度的题目,都需要主定理进行运算。

前置

我们常见的程序时间复杂度有:

O(n)/O(n2)/O(nlog2n)/O(2n)O(n)/O(n^2)/O(nlog_2n)/O(2^n) 等等...

我们叫它程序的渐进时间复杂度,例如一段程序执行这样的循环:

for(int k=1;k<=n;k++)
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);
for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
        sum+=a[i][j];
for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
        pai*=a[i][j];

显然,这段代码一共运行了 n3+2n2n^3+2n^2 次,我们将它的渐进时间复杂度写作 O(n3)O(n^3),即保留最高项但忽略其系数。

约定:一般我们将 log2nlog_2n 写作 lognlogn ,将 lognlognlogn\cdot logn 写作log2nlog^2n

算时间复杂度有什么用呢?一般来说,在比赛时我们将知道程序的时间限制,一般为 1s1s ,我们可以通过粗略计算程序时间复杂度来判断程序是否能在 1s1s 通过,否则会 TLE\text{TLE}Time Limit Exceeded\text{Time Limit Exceeded})。

时间复杂度 1s1s内稳过的范围 极限范围(危险)
O(1)O(1) \infty \infty
O(wys)O(wys) \infty \infty
O(logn)O(logn) \infty \infty
O(n)O(n) 5×1075\times10^7 10810^8
O(nlogn)O(nlogn) 5×1055\times 10^5 10610^6
O(n2)O(n^2) 50005000 1000010000
O(n3)O(n^3) 300 500
O(2n)O(2^n) 25 27
O(n!)O(n!) 11 11(稳过)
O(nn)O(n^n) 8 8(稳过)

PS:由于程序存在常数因子,极限范围不一定能过,除非你是欧洲人

大概来说,如果你算出的渐进时间复杂度量化后在千万级别[ n×107n\times10^7 ],基本上是很稳的

对于非递归程序时间复杂度的运算方法,比较简单粗暴的方法是数循环。但这种方法并不一定始终正确,如 NOIP2017PJT4\text{NOIP2017PJT4} 的二分答案是 O(logn)O(logn) 复杂度的,dp\text{dp} 的转移是执行 nn 次的,而对于单调队列,每个元素至多进队一次,出队一次,最多有 2n2n 次操作,dp\text{dp} 的总操作次数应该是将它们加在一起,共 3n3n 次操作,时间复杂度为 O(n)O(n),而不是 O(n2)O(n^2),总复杂度为 O(nlogn)O(nlogn) ,与之类似的还有 kmpkmp 算法的时间复杂度。(PS: kmp\text{kmp} 算法的时间复杂度至今仍存在争议,我们一般将其视作 O(n)O(n)

正文

介绍 master\text{master} 定理前,首先要知道几个符号

  1. T(n)T(n) 表示时间复杂度,可以这样表示:T(n)=T(n)=一个单项式,例如:

T(n)=2T(n/2)+f(n)T(n)=2T(n/2)+f(n)

  1. Θ\Theta 读音:thetatheta,表示等于
  2. OO 读音:big ohbig\ oh,表示小于等于
  3. oo 读音:small ohsmall\ oh,表示小于
  4. Ω\Omega 读音:big omegabig\ omega,表示大于等于
  5. ω\omega 读音:small omegasmall\ omega,表示大于

主定理是怎么表示的呢?

  • 我们目前有一个规模为nn的问题
  • 通过分治,我们将问题分成 aa 个规模为 nb\frac{n}{b},每次递归将带来 f(n)f(n) 的额外计算
  • 于是得到关系式:

T(n)=aT(nb)+f(n)T(n)=aT(\frac{n}{b})+f(n)

此外,我们还要定义一个 CcritC_{crit},它是这样计算的:

Ccrit=logbaC_{crit}=log_ba

那么有:

11.

  • f(n)=O(nc)f(n)=O(n^c),且 c<ccritc<c_{crit} 时有:T(n)=Θ(nccrit)T(n)=\Theta(n^{c_{crit}})
  • 例子:
  • T(n)=8T(n2)+1000n2T(n)=8T(\frac{n}{2})+1000n^2
  • 此时 a=8,b=2,f(n)=1000n2a=8,b=2,f(n)=1000n^2
  • c=2c=2 时,f(n)=O(n2)f(n)=O(n^2)
  • 又因为 Ccrit=logba=3>cC_{crit}=log_ba=3>c
  • 所以 T(n)=Θ(nlogba)=Θ(n3)T(n)=\Theta(n^{log_ba})=\Theta(n^3)

22.

  • f(n)=O(nc)f(n)=O(n^c),且 c>ccritc>c_{crit} 时有:T(n)=Θ(f(n))T(n)=\Theta(f(n))
  • 例子:
  • T(n)=2T(n2)+n2T(n)=2T(\frac{n}{2})+n^2
  • 此时 a=2,b=2,f(n)=n2a=2,b=2,f(n)=n^2
  • c=2c=2 时,f(n)=O(n2)f(n)=O(n^2)
  • 又因为 ccrit=logba=1<cc_{crit}=log_ba=1<c
  • 所以 T(n)=Θ(f(n))=Θ(n2)T(n)=\Theta(f(n))=\Theta(n^2)

33.

  • 若存在一个非负整数 kk,使得 f(n)=Θ(nccritlogkn)f(n)=\Theta(n^{c_{crit}}log^kn)
  • 那么 T(n)=Θ(nccritlogk+1n)T(n)=\Theta(n^{c_{crit}}log^{k+1}n)
  • 例子:
  • T(n)=2T(n2)+10nT(n)=2T(\frac{n}{2})+10n
  • 此时 a=2,b=2,f(n)=10na=2,b=2,f(n)=10nccrit=logba=1c_{crit}=log_ba=1
  • k=0k=0f(n)=Θ(n1log0n)=Θ(n)f(n)=\Theta(n^1log^0n)=\Theta(n)
  • 所以 T(n)=Θ(n1log1n)=Θ(nlogn)T(n)=\Theta(n^1log^1n)=\Theta(nlogn)

练手题

  • 首先,我们知道 a=2,b=4,f(n)=n=n12a=2,b=4,f(n)=\sqrt{n}=n^{\frac{1}{2}}c=12,ccrit=log42=12c=\frac{1}{2},c_{crit}=log_42=\frac{1}{2}
  • k=0k=0 时,满足条件3,所以,T(n)=O(nlogn)T(n)=O(\sqrt nlogn),选 CC

T(n)T(n)
=T(n1)+n1+n=T(n-1)+n-1+n
=T(n2)+n2+n1+n=T(n-2)+n-2+n-1+n
=...=...
=T(0)+0+1+2+...+n2+n1+n=T(0)+0+1+2+...+n-2+n-1+n
=1+n×(n+1)2=1+\frac{n\times (n+1)}{2}
=O(n2)=O(n^2)

选择 DD


  • 假设 g(i)g(i),为计算 F(i)F(i) 的次数,因为 F(i)=F(i1)+F(i2)F(i)=F(i-1)+F(i-2) 所以 g(i)=g(i1)+g(i2)g(i)=g(i-1)+g(i-2)
  • 因为 F(1)=g(1)=1,F(2)=g(2)=1F(1)=g(1)=1,F(2)=g(2)=1,所以 g(n)=F(n)g(n)=F(n)
  • 则:

T(n)=g(n)=F(n)=O(F(n))T(n)=g(n)=F(n)=O(F(n))

选择 DD

完结

这和大家能够平安过初赛!