预备

0.1 渐进符号

其实不少高等数学 / 数学分析教材在讲解无穷小的比较时已经相当严谨地介绍过大 O、小 O 记号,然而各种历史习惯记法的符号滥用(abuse of notation)[1] 直到现在都让笔者头疼. These notations seem to be innocent, but can be catastrophic without careful manipulation. For example,

  • n=O(n2)∧n2=O(n2)⟹n=n2

    Knuth 在《具体数学》里举出的例子[2]. “=” 隐含的对称性使其在 g(x)=O(f(x)) 中格格不入. 事实上,将 O(f(x)) 看作“阶不高于 f(x) 的所有函数的集合”是比“某个阶不高于 f(x) 的函数”更严谨的理解. 因此,本文将使用 f(x)∈O(g(x)) (有时也记为 O(f(x))⊂O(g(x)))的集合论符号代替传统的 f(x)=O(g(x)) 记法.

  • n2sin⁡n∈O(n2)⟹∑i=1ni2sin⁡i∈∑i=1nO(i2)⊂O(∑i=1ni2)⊂O(n3) 或更一般的, g(x)∈O(f(x))⟹∑P(n,i)g(i)∈∑P(n,i)O(f(i))⊂O(∑P(n,i)f(i))

    没看出有啥问题,对吧?笔者在写作此文时犯了同样的错误. 请注意,大 O 记号的作用对象是函数,f(i) 是什么?它只是个函数值,是确定的数——这是因为 i 也是求和枚举中确定的数,而不是 n 这种真正代表变元的记号. 所以 O(f(i)) 是什么?它什么也不是.

    这种错误的出现是在所难免的,我们太习惯用 x、x3+5x2+x 这种变元都不明确的记号来表示函数了[1] . 写成 f(x) 也不严谨,因为只有 f 才应代表函数本身,f(x) 只能是函数值. 这样我们就可以放心地写下 O(f),不用担心把变元与确定值弄混了.

    然而大家还是喜欢写 O(n2) 和 O(en2),而不是奇怪的 O(id2) 和 O(exp∘id2). 所以,我们大概只能沿用这种不太严谨的记号,并时刻提醒自己加倍小心了. (形如 x↦ex2 的 λ 风格“匿名函数”记号可能更好?)

    但上述命题从结论上是正确的. 正确的推导过程应为 ∑P(n,i)g(i)≤∑P(n,i)Cf(i)≤C∑P(n,i)f(i)∈O(∑P(n,i)f(i))

    第一步是直接由大 O 记号的定义得到的结果.

Wikipedia[3] 中有一张详尽的表格介绍了各种渐进符号的定义,OI Wiki[4] 上也有极好的讲解,尚不熟练的读者可以参考. 有兴趣仔细研究的读者可以参考《具体数学》第九章[2] 、Wikipedia 及其 reference(个人推荐 Knuth 关于 O、Ω、Θ 的短文[5] ). 本文除用 “∈” 和“⊂”替代 “=” 外,完全使用 Knuth 提议的记号体系.

0.2 调和数 H(n) / 调和级数

调和级数的部分和 H(n) 定义为 H(n)=∑i=1n1i 通过一些与 e 有关的数列放缩可以证明 limn→∞(H(n)−log⁡n)=c,其中 c≈0.577 是 Euler 常数. 因此 nH(n)∼nlog⁡n∈Θ(log⁡n).

0.3 自然数等幂和 Pp(n) / p - 级数

p - 级数可视为调和级数的推广. 其部分和定义为 Pp(n)=∑i=1ni−p

p - 级数具有如下性质:

  • 当 p>1 时,p - 级数收敛;

  • 当 p=1 时,p - 级数是调和级数;

  • 当 −∞<p<1 时,我们指出 Pp(n)∼11−pn1−p∈Θ(n1−p)

−∞<p<1 时 p - 级数的渐进估计可以从连续幂函数积分的角度理解. 证明这渐进性,离散情况下,可对 np 差分后前缀和 + 二项式定理得到高次项系数,或可用离散微积分理论得到精确表示(参见《具体数学》[6] );连续情况下,Lagrange 中值定理应为较简单的估计方法. 这里从略. 总之,我们得到: Pp(n)∈{Θ(n1−p)p<1Θ(nlog⁡n)p=1Θ(1)p>1

1 约数函数 σz(n)

约数函数(Divisor Function,也可称为除数函数、因数函数)是与 n 的因子有关的一类函数,定义如下:

Definition 1 (约数函数) σz(n)=∑d∣ndz

当 z=0 时,σ0(n) 被称为约数个数函数(number-of-divisors function),常被记为 d(n) 或 τ(n). 当 z=1 时,σ1(n) 被称为约数和函数(sum-of-divisors function),常直接记为 σ(n).

Example 1 估计 σ0(n) 的渐进上界.

也就是估计 n 的因子的数量. 一个广为人知的上界是 2n,因为 n 的所有小于 n 的因子 d 均与另一因子 nd 一一对应.

事实上进一步可以证明 σ0(n)∈o(nϵ)∀ϵ>0[7] ,虽然这在 OI 中并不实用.

Example 2 估计 σ0^(n)=∑i=1nσ0(i) 的渐进上界.

即估计 1 到 n 中所有数因子个数的和. 这是一个形式上鲜为人知但其应用广为人知的例子. 变换求和顺序,容易得到

σ0^(n)=∑i=1nσ0(i)=∑i=1n∑d∣i1=∑d=1n⌊nd⌋≤∑d=1nnd=nH(n)∈O(nlog⁡n)

显然,这比 O(nn) 的平凡估计好上不少. 本例的思路不仅是埃氏筛(Sieve of Eratosthenes)的理论基础,也在杜教筛、快速 Mobius 变换、gcd 卷积[8] 等处出现.

进一步利用此技巧和 p - 级数的估计,我们甚至能在仔细研究 σz(n) 前就得到其前缀和的渐进估计:

Example 3 估计 σz^(n)=∑i=1nσz(i) 的渐进上界.

σz^(n)=∑i=1nσz(i)=∑i=1n∑d∣idz=∑d=1ndz⌊nd⌋≤n∑d=1ndz−1=nP1−z(n)∈{O(nz+1)z>0O(nlog⁡n)z=0O(n)z<0

遗憾的是,对此前缀和做差分并不能得到 σz(n) 的优秀估计.

现在引入一个重要放缩技巧,其在后续估计中屡试不爽.

Proposition 1 ∑d∣nf(d)≤∑i=1nf(⌊ni⌋)

显然,右式比左式多算了 i∤n 的项,因此命题是正确的. 但我们还可以做得更好:

Proposition 2 ∑d∣nf(d)≤∑i=1nf(i)+f(⌊ni⌋)

n 分治. 我们其实已经在 Example 1 估计 σ0(n) 时用过此技巧了.

Example 4 估计 σ1(n) 的渐进上界.

Proposition 1: σ1(n)=∑d∣nd≤∑i=1n⌊ni⌋≤nH(n)∈O(nlog⁡n)

可以证明用 Proposition 2 不会得到更优的结果.

我们发现了一个有趣的事实:σ1(n) 和 σ0^(n) 的渐进上界均为 O(nlog⁡n).

Example 5 估计 σz(n) 的渐进上界.

Proposition 2 和 p - 级数的性质:

σz(n)=∑d∣ndz≤∑i=1niz+⌊ni⌋z≤{2∑i=1n⌊ni⌋z≤2nz∑i=1ni−z=2nzPz(n)z≥02∑i=1niz=2P−z(n)z<0∈{2nzO(1)z>12nO(log⁡n)z=12nzO(n1−z2)0≤z<12O(n1+z2)−1<z<02O(log⁡n)z=−12O(1)z<−1={O(nz)z>1O(nlog⁡n)z=1O(n1+z2)−1<z<1O(log⁡n)z=−1O(1)z<−1

我们得到了一个相当优秀的渐进上界. 值得关注的是:

  • 当 z=0 时,σ0(n)∈O(n12). 这与 Example 1 的结果一致.
  • 当 z=12 时,σ12(n)∈O(n34),即 ∑d∣nd∈O(n34). 洛谷 P4980 Polya 定理模板题[9] 的一种比较 trivial 的解法[10] 的时间复杂度证明就来源于此. 我们之后还会在整除分块与杜教筛中见到它.

另外,如果只使用 Proposition 1 ,−1<z<1 部分的渐进上界将只能估计至 O(n). 因此 Proposition 2 是更为优越的.

约数函数更复杂的上限与渐进估计可参考 Wikipedia[7].

2 整除分块

也被称为数论分块. 求 ∑i=1nf(i)g(⌊ni⌋) 我们按 d=⌊ni⌋ 分块求和: ∑dg(d)∑⌊ni⌋=df(i) 可以证明,对一指定的 d,满足 d=⌊ni⌋ 的 i 取遍一连续区间,故若 f 的前缀和能 O(1) 求出,块数量 #{⌊ni⌋}i=1n 即该算法的时间复杂度. 注意到当 i≤n 时,⌊ni⌋ 最多只有 ⌊n⌋ 种取值,而 i≥n 时,1≤⌊ni⌋≤n 表明其也最多只有 ⌊n⌋ 种取值. 因此整除分块的时间复杂度 T1(n)=#{⌊ni⌋}i=1n≤2n∈O(n)

方便起见,后文记 D(n)={⌊ni⌋}i=1n.

2.1 整除分块嵌套

Proposition 2 加强,我们有如下通用放缩:

Proposition 3 ∑d∣nf(d)≤∑d∈D(n)f(d)≤∑i=1nf(i)+f(⌊ni⌋)

LHS 成立的关键在于 {d:d∣n}⊂D(n);而 RHS 的本质就是上述对整除分块块数量上界的估计.

注意到 Proposition 2Example 5 证明的核心,而 Proposition 3Proposition 2 的加强版,故仿造 Example 5 的证明,我们有

Example 6 令 Sz(n)=∑d∈D(n)dz 则前述 Example 5 中 σz(n) 的上界与渐进上界也同样适用于 Sz(n).

现在可以对嵌套整除分块 ∑i=1nf(i)∑j=1⌊ni⌋g(j)h(⌊nij⌋) 的时间复杂度 T2 做出估计了. 对 Example 6 取 z=12,立刻有 T2(n)=∑d∈D(n)T1(d)≤2∑d∈D(n)d=2S12(n)≤4nP12(n)∈O(n34)

我们还可以进一步归纳. 假定 ∀m≥0,∃zm:0≤zm<1,Tm(n)=O(nzm),我们有

Tm+1(n)=∑d∈D(n)Tm(d)≤C∑d∈D(n)nzm=CSzm(n)∈O(n1+zm2)

因此 zm+1=1+zm2. 边界条件 z0=0,数列递推求得 zm=1−2−m,检验满足条件. 因此 m 重嵌套整除分块的时间复杂度 Tm(n)∈O(n1−2−m)

3 杜教筛

杜教筛可以以低于线性的时间复杂度求解某些数论函数的前缀和. 其思路并不复杂. 设 f 为一数论函数,我们希望快速求得其前缀和 f^(n)=∑i=1nf(i). 考虑数论函数 g 和 h=g∗f, h(n)=∑d∣ng(d)f(nd) 两端做前缀和得 h^(n)=∑i=1nh(i)=∑i=1n∑d∣ig(d)f(id)=∑d=1ng(d)∑i=1⌊nd⌋f(i)=∑d=1ng(d)f^(⌊nd⌋)=g(1)f^(n)+∑d=2ng(d)f^(⌊nd⌋) 因此 f^(n)=1g(1)(h^(n)−∑d=2ng(d)f^(⌊nd⌋))

故若 g、h 的前缀和可 O(1) 算得,根据上式整除分块即可递归地计算出 f 的前缀和.

下面分析算法的复杂度. 注意到 ⌊⌊ni⌋j⌋=⌊nij⌋ 故单轮递归涉及到的自变量均可表示为 d=⌊ni⌋ 的形式. 一个 f^(d) 做整除分块耗时 T1(d),若采用记忆化递归,由上节分析,算法总时间复杂度为 ∑d∈D(n)T1(d)=T2(n)∈O(n34)

但我们还可以做得更好——考虑先用 O(K) 的时间复杂度线性筛出前 K 个 f(n) 并求前缀和,则递归求解时,d≤K 的 f^(d) 就无需再向下递归了. 为分析此类时间复杂度,对 Proposition 3 做最后一点扩展:

Proposition 4 ∑d∣nd>Kf(d)≤∑d∈D(n)d>Kf(d)≤∑K<i≤nf(i)+∑1≤i≤min{⌊nK⌋,n}f(⌊ni⌋)

特别的,当 K>n 时,有

∑d∣nd>Kf(d)≤∑d∈D(n)d>Kf(d)≤∑1≤i≤⌊nK⌋f(⌊ni⌋)

故用 Proposition 4 ,当 K>n 时,算法在递归部分的时间复杂度降低为

∑d∈D(n)d>KT1(d)=∑1≤i≤⌊nK⌋T1(⌊ni⌋)≤∑1≤i≤⌊nK⌋Cni=Cn∑1≤i≤⌊nK⌋i−12=CnP12(⌊nK⌋)∈nO((nK)12)⊂O(nK−12)

总时间复杂度为 O(K)+O(nK−12)

为最小化时间复杂度,取 K=n23,得到最优时间复杂度 O(n23).

这部分的时间复杂度证明主要参考了文章[11].

References

1. Abuse of notation - wikipedia. (n.d.). https://en.wikipedia.org/wiki/Abuse_of_notation#Function_notation.
2. Graham, R. L., Knuth, D. E., & Patashnik, O. (1994). Concrete mathematics: A foundation for computer science (second, pp. 443–449). Addison-Wesley.
3. Big o notation - wikipedia # family of bachmann–landau notations. (n.d.). https://en.wikipedia.org/wiki/Big_O_notation#Family_of_Bachmann%E2%80%93Landau_notations.
5. Knuth, D. E. (1976). Big omicron and big omega and big theta. SIGACT News, 8(2), 18–24. https://doi.org/10.1145/1008328.1008329
6. Graham, R. L., Knuth, D. E., & Patashnik, O. (1994). Concrete mathematics: A foundation for computer science (second, pp. 47–56). Addison-Wesley.
7. Divisor function - wikipedia # growth_rate. (n.d.). https://en.wikipedia.org/wiki/Divisor_function#Growth_rate.
8. sun123zxy. (2020). sun123zxy’s blog - 原创OI题目 GCD卷积 problem and solution. https://blog.sun123zxy.top/posts/20201206-gcdconv/.
9. P4980 【模板】pólya 定理 - 洛谷 | 计算机科学教育新生态. (n.d.). https://www.luogu.com.cn/problem/P4980.
10. sun123zxy. (2020). sun123zxy’s blog - 等价类计数:Burnside引理 & Polya定理. http://blog.sun123zxy.top/posts/20200321-burnside/#s-4.3.
11. Ander. (2022). 杜教筛. https://zhuanlan.zhihu.com/p/521699400.

OI 数论中的上界估计与时间复杂度证明的更多相关文章

  1. SQL Server中关于基数估计如何计算预估行数的一些探讨

    关于SQL Server 2014中的基数估计,官方文档Optimizing Your Query Plans with the SQL Server 2014 Cardinality Estimat ...

  2. [HRBUST-1688]数论中的异或(思维题)

    数论中的异或 Time Limit: 1000 MS Memory Limit: 32768 K Total Submit: 75(41 users) Total Accepted: 35(30 us ...

  3. 毫米波大规模阵列中的AOA估计

    1.AOA估计在毫米波大规模MIMO中的重要性 在毫米波大规模MIMO的CSI估计中,AoA估计具有重要地位,主要原因归纳如下: 毫米波大规模MIMO 的信道具有空域稀疏性,可以简单通过AoA 和路径 ...

  4. 文献名:Repeat-Preserving Decoy Database for False Discovery Rate Estimation in Peptide Identication (用于肽段鉴定中错误发生率估计的能体现重复性的诱饵数据库)

    文献名:Repeat-Preserving Decoy Database for False Discovery Rate Estimation in Peptide Identication (用于 ...

  5. 一些求和式的估算 & 杜教筛时间复杂度证明

    本文内容概要: \(A=\sum\limits_{i=1}^n\dfrac1{\sqrt i}=1+\dfrac1{\sqrt2}+\cdots+\dfrac1{\sqrt n}\) \(O(\sqr ...

  6. acm数论之旅--欧拉函数的证明

    随笔 - 20  文章 - 0  评论 - 73 ACM数论之旅7---欧拉函数的证明及代码实现(我会证明都是骗人的╮( ̄▽ ̄)╭) https://blog.csdn.net/chen_ze_hua ...

  7. $\mathcal{OI}$生涯中的各种数论算法的证明

    嗯,写这个是因为我太弱了\(ORZ\). #\(\mathcal{\color{silver}{1 \ \ Linear \ \ Sieve \ \ Method \ \ of \ \ Prime}} ...

  8. Java中ArrayList和LinkedList区别 时间复杂度 与空间复杂度

    一般大家都知道ArrayList和LinkedList的大致区别:      1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构.      2.对于随机访问 ...

  9. 视觉SLAM中的深度估计问题

    一.研究背景 视觉SLAM需要获取世界坐标系中点的深度. 世界坐标系到像素坐标系的转换为(深度即Z): 深度的获取一共分两种方式: a)主动式 RGB-D相机按照原理又分为结构光测距.ToF相机 To ...

  10. OI 数论整理

    1.素数: 质数(prime number)又称素数,有无限个.一个大于1的自然数,除了1和它本身外,不能被其他自然数整除,换句话说就是该数除了1和它本身以外不再有其他的因数;否则称为合数. 2016 ...

随机推荐

  1. XML_DTD_20200415

    <!-- xml的注释写法 --> 格式良好的xml语言必须具备的几个条件  1.必须有xml声明语句,声明版本号与编码字符集 2.必须有且仅有一个根元素 3.标签大小写敏感  4.属性值 ...

  2. GDB 调试 - 正确地加载调试符号文件

    一.开发流程 1. 编译可执行文件 1 #include <stdio.h> 2 #include <unistd.h> 3 4 void test() 5 { 6 char ...

  3. revit转tileset 3dmax转tileset cesium展示

    使用revit软件导出fbx模型: 使用glTFExport导出gltf模型,导出的gltf模型具有属性.但是此处导出gltf模型,不是为了在cesium中加载该gltf模型,主要目的是获取组件属性信 ...

  4. Cross Site Scripting DOM (XSS) 攻击jQuery append() 的处理方法

    做安全红线使用Fortify工具进行扫描时,jquery append会报Cross Site Scripting DOM风险.解决该问题有两种办法. 一.原生dom方式 使用JavaScript原生 ...

  5. java的死锁与解决方法

    一.什么是死锁? 死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无限等待. 二.产生死锁的原因与四个条件 2.1 死锁原因 竞争资 ...

  6. 开学考--MIS系统(javaweb的开学练习--网络新闻发布系统)

    关于本次考试的相关理解 看到题目的时候,第一反应是这道题不难,之前已经做过十分类似的题目了,然后对于难度是很有自信的(当然,对于用户的权限管理部分,还是很懵): 而第二反应就是,题量挺大的,我在这有限 ...

  7. Python--相关环境的安装,以及hello world的实现

    相关环境 进入官网:https://www.python.org/downloads/ 点击这里: 来到新的界面之后,向下滑动: 找到上图中的界面,选择版本进行下载即可. 具体的安装步骤可以参考这里看 ...

  8. Activiti7开发(二)-流程定义

    目录 1.部署流程模型为流程定义 2.挂起/激活流程定义 3.删除流程定义 4.查询流程定义 5.上传并部署流程定义 6.查看流程模型 1.部署流程模型为流程定义 @PostMapping(value ...

  9. windows 系统下 workerman 在同一个运行窗口中开启多个 websocket 服务

    目录 开启多个 ws 服务失败 开启服务失败解决办法 同一个窗口中运行 开启多个 ws 服务失败 正常情况下,如果你想开启多个 websocket 服务的话 只要在一个文件中,输入 new Worke ...

  10. Android 音视频采集那些事

    音视频采集 在整个音视频处理的过程中,位于发送端的音视频采集工作无疑是整个音视频链路的开始.在 Android 或者 IOS 上都有相关的硬件设备--Camera 和麦克风作为输入源.本章我们来分析如 ...