算法设计与分析


1. Introduction

1.1. Stirling 公式

𝑛!=2𝜋𝑛(𝑛𝑒)𝑛(1+Θ(1𝑛))

𝑛!=𝑜(𝑛𝑛)𝑛!=𝜔(2𝑛)log(𝑛!)=Θ(𝑛log(𝑛))

1.2. 取整函数

⌊⌊𝑛𝑎⌋𝑏⌋=⌊𝑛𝑎𝑏⌋⌈⌈𝑛𝑎⌉𝑏⌉=⌈𝑛𝑎𝑏⌉

2. Algoritnm Basis

2.1. 序列求和

∑𝑘=1𝑛1𝑘=log𝑛+𝑂(1)∑𝑡=1𝑘𝑡2𝑡−1=2(∑𝑡=1𝑘𝑡2𝑡−1)−∑𝑡=1𝑘𝑡2𝑡−1=∑𝑡=1𝑘𝑡2𝑡−∑𝑡=1𝑘𝑡2𝑡−1=𝑘2𝑘−1+∑𝑡=1𝑘−1(𝑡2𝑡−(𝑡+1)2𝑡)=(𝑘−1)2𝑘+1

2.2. 迭代法求解递推方程(汉诺塔问题为例)

py
1
def hanoi(A, C, n):
2
    if n == 1: 
3
        move(A, C)
4
    else:
5
        hanoi(A, B, n-1)
6
        move(A, C)
7
        hanoi(B, C, n-1)
1
def hanoi(A, C, n):
2
    if n == 1: 
3
        move(A, C)
4
    else:
5
        hanoi(A, B, n-1)
6
        move(A, C)
7
        hanoi(B, C, n-1)
𝑇(𝑛)=2𝑇(𝑛−1)+1=2(2𝑇(𝑛−2)+1)+1⋮=2𝑛−1𝑇(1)+2𝑛−2+⋯+2+1=2𝑛−1

2.3. 差消法化简递推方程(快排为例)

py
1
def qsort(arr, start, end):
2
    if end - start <= 1: return
3
    mid = split(arr, start, end)
4
    qsort(arr, start, mid)
5
    qsort(arr, mid+1, end)
1
def qsort(arr, start, end):
2
    if end - start <= 1: return
3
    mid = split(arr, start, end)
4
    qsort(arr, start, mid)
5
    qsort(arr, mid+1, end)
𝑇(𝑛)=2𝑛∑𝑖=0𝑛−1𝑇(𝑖)+(𝑛−1)𝑛𝑇(𝑛)=2∑𝑖=0𝑛−1𝑇(𝑖)+𝑛(𝑛−1)(𝑛−1)𝑇(𝑛−1)=2∑𝑖=0𝑛−2𝑇(𝑖)+(𝑛−1)(𝑛−2)𝑛𝑇(𝑛)−(𝑛−1)𝑇(𝑛−1)=2𝑇(𝑛−1)+2(𝑛−1)𝑛𝑇(𝑛)=(𝑛+1)𝑇(𝑛−1)+2(𝑛−1)𝑇(𝑛)𝑛+1=𝑇(𝑛−1)𝑛+2𝑛−1𝑛(𝑛+1)=Θ(∑𝑘=1𝑛1𝑘)=Θ(log𝑛)𝑇(𝑛)=Θ(𝑛log𝑛)

2.4. 主定理

𝑇(𝑛)=𝑎𝑇(𝑛𝑏)+𝑓(𝑛)
  1. 若 ∃𝜀>0,𝑓(𝑛)=𝑂(𝑛log𝑏𝑎−𝜀) 则

    𝑇(𝑛)=Θ(𝑛log𝑏𝑎)
  2. 若 𝑓(𝑛)=Θ(𝑛log𝑏𝑎) 则

    𝑇(𝑛)=Θ(𝑛log𝑏𝑎log𝑛)
  3. 若 ∃𝜀,𝑓(𝑛)=Ω(𝑛log𝑏𝑎+𝜀) 且 ∃𝑐<1,∃𝑛0,∀𝑛≥𝑛0,𝑎𝑓(𝑛𝑏)≤𝑐𝑓(𝑛) 则

    𝑇(𝑛)=Θ(𝑓(𝑛))

不能使用主定理的例子 𝑇(𝑛)=2𝑇(𝑛2)+𝑛log𝑛

𝑛log22=𝑛1∄𝜀>0 s.t. 𝑛log𝑛=Ω(𝑛1+𝜀)

3. Divide and Conquer

3.1. 分治算法改进:减少子问题数量

𝑇(𝑛)=4𝑇(𝑛2)+𝑂(𝑛)⇒𝑇(𝑛)=3𝑇(𝑛2)+𝑂(𝑛)

3.2. 分治算法改进:增加预处理

作用是减少 𝑓(𝑛)


4. Classic Divide and Conquer

4.1. 一般性质选择问题

5个一组分别排序,每个组的中位数的集合取中位数 𝑚⋆ ,根据 𝑚⋆ 划分子问题,根据子问题的规模大小与 𝑘 的大小关系排除部分子问题

𝑊(𝑛)=𝑊(𝑛3)+𝑊(23𝑛)+𝑂(𝑛)

𝑊(𝑛)=𝑊(𝑛5)+𝑊(710𝑛)+𝑂(𝑛)

4.2. 卷积

𝑎∗𝑏=(𝑐0𝑐1⋮𝑐𝑘⋮𝑐𝑚+𝑛)=(𝑎0𝑏0𝑎0𝑏1+𝑎1𝑏0⋮∑𝑖+𝑗=𝑘𝑎𝑖𝑏𝑗⋮𝑎𝑚𝑏𝑛)

4.3. 多项式系数

𝐴(𝑥)=𝑎0+𝑎1𝑥+⋯𝑎𝑚−1𝑥𝑚𝐵(𝑥)=𝑏0+𝑏1𝑥+⋯𝑏𝑛−1𝑥𝑛𝐴(𝑥)𝐵(𝑥)=𝑎0𝑏0+(𝑎1𝑏0+𝑎0𝑏1)𝑥+⋯+(𝑎𝑚−1𝑏𝑛−1)𝑥𝑚+𝑛=𝑐0+𝑐1𝑥+⋯𝑐𝑚+𝑛−2𝑥𝑚+𝑛where 𝑐=𝑎∗𝑏

4.4. 分治法多项式求值

assume that 𝑚 is odd

𝐴(𝑥)=𝑎0+𝑎1𝑥+⋯𝑎𝑚−1𝑥𝑚−1𝐴(𝑥)=𝐴 even (𝑥2)+𝑥𝐴 odd (𝑥2) where 𝐴 even (𝑥)=𝑎0+𝑎2𝑥+⋯+𝑎𝑚−2𝑥𝑚−22𝐴 odd (𝑥)=𝑎1+𝑎3𝑥+⋯+𝑎𝑚−1𝑥𝑚−22𝑇(𝑛)=2𝑇(𝑛2)+𝑂(𝑛)𝑇(𝑛)=𝑂(𝑛log𝑛)

4.5. FFT

求多项式 𝐴(𝑥) 和 𝐵(𝑥) 的乘积 𝐶(𝑥) 选择2𝑛个𝑥的值(选择1的2𝑛次根),求出𝐴(𝑥𝑗)和𝐵(𝑥𝑗)的值,再用求出𝐶(𝑥𝑗)=𝐴(𝑥𝑗)𝐵(𝑥𝑗),最后多项式插值求出𝐶(𝑥)

  1. 对每个𝑥𝑗计算𝐴(𝑥𝑗)和𝐵(𝑥𝑗)的值𝑂(𝑛log𝑛)
  2. 对每个𝑥𝑗计算𝐶(𝑥𝑗)的值𝑂(𝑛)
  3. 定义 𝐷(𝑥)=𝐶(𝑥0)+𝐶(𝑥1)𝑥+⋯+𝐶(𝑥2𝑛−1)𝑥2𝑛−1
  4. 计算 𝐷(𝑥𝑗)𝑂(𝑛log𝑛)
  5. 2𝑛𝑐2𝑛−𝑗=𝐷(𝑥𝑗)𝑂(𝑛)
𝑇(𝑛)=𝑂(𝑛log𝑛)

5. Dynamic Programming

5.1. 矩阵链相乘

另 𝑑[𝑖,𝑗] 为 𝑥𝑖⋅𝑥𝑖+1⋅⋯⋅𝑥𝑗 的最小开销

𝑑[𝑖,𝑗]={0 if 𝑖=𝑗min𝑖≤𝑘<𝑗(𝑑[𝑖,𝑘]+𝑑[𝑘+1,𝑗]+𝑝𝑖−1𝑝𝑘𝑝𝑗) otherwise

5.2. 投资问题

𝑥 元投资给 𝑚 个项目,每个项目的收益为 𝑓𝑖(𝑥)

dp[𝑥,𝑘]={𝑓1(𝑥) if 𝑘=1max𝑡=0,…,𝑥(dp[𝑥−𝑡,𝑘−1]+𝑓𝑘(𝑡)) otherwise

5.3. 背包问题

𝑛种物品,可以拿多个,重量为 𝑤𝑖,价值为𝑣𝑖,背包容量为𝑏,求最大价值

𝑑[𝑥,𝑘]={−∞ if 𝑥<00 if 𝑥=0 or 𝑘=0max(𝑑[𝑥−𝑤𝑘,𝑘]+𝑣𝑘,𝑑[𝑥,𝑘−1]) otherwise

也可以写成投资问题那样,每一步都对拿几个进行迭代,但是时间复杂度更差

5.4. 最长公共子序列

给定 𝑋=(𝑥1,𝑥2,…𝑥𝑚) 和 𝑌=(𝑦1,𝑦2,…,𝑦𝑛)

5.5. 最长上升子序列

给定 𝑋=(𝑥1,𝑥2,…,𝑥𝑛)

令 𝑑[𝑖] 表示以 𝑥𝑖 结尾的最长上升子序列

𝑑[𝑖]=max1≤𝑗<𝑖 and𝑥𝑗<𝑥𝑖𝑑[𝑗]+1

(考虑到 𝑑[𝑗] 是递增的,也可以二分查找优化下)


6. Applications of DP

6.1. 图像压缩

给定 𝑃=(𝑝1.𝑝2,…,𝑝𝑛),对 𝑃 进行分段,每个分段中每个数字占用的存储空间是 𝑏(分段中的最大值 )+11,求占用空间最小的分段方式

令 𝑑[𝑖] 表示以 𝑝𝑖 为结尾的数据的最优分段占用

𝑑[𝑖]=min𝑗≤max(𝑖,255)(𝑑[𝑖−𝑗]+𝑗𝑏(max𝑖−𝑗<𝑘≤𝑖(𝑝𝑘))+11)

6.2. 最大字段和

给定 𝐴=(𝑎1,𝑎2,…,𝑎𝑛),求 𝐴 的连续子序列的和的最大值

令 𝑑[𝑖] 表示以 𝑎𝑖 为结尾的子序列的和的最大值

𝑑[𝑖]=max(𝑑[𝑖−1],0)+𝑎𝑖

6.3. RNA二级结构预测

有点像矩阵链相乘,但是𝑘的切分更复杂一点,这里𝑘需要和𝑗配对,再单独考虑𝑗不会配对的情况

6.4. 编辑距离

给定 𝑋=(𝑥1,𝑥2,…,𝑥𝑚) 和 𝑌=(𝑦1,𝑦2,…,𝑦𝑛),对 𝑋 进行插入,删除和替换操作,将𝑋变为𝑌的最少操作次数

𝑑[𝑖,𝑗]={max(𝑖,𝑗) if 𝑖𝑗=0𝑑[𝑖−1,𝑗−1] if 𝑥𝑖=𝑦𝑗max(𝑑[𝑖,𝑗−1],𝑑[𝑖−1,𝑗],𝑑[𝑖−1,𝑗−1])+1 otherwise

7. Greedy Algoritnm

7.1. 活动选择问题最优性证明

命题:在算法执行到第𝑘步时,选择了𝑘个活动,最优解𝐴包含 1,𝑖2,…,𝑖𝑘

归纳基础
最优解A中包含1
归纳步骤

已知最优解中包含 1,𝑖2,…,𝑖𝑘,证明最优解包含 1,𝑖2,…,𝑖𝑘,𝑖𝑘+1

另剩下的问题叫做𝑆′,𝐵为𝐴−{1,𝑖2,…,𝑖𝑘}
可以证明𝐵是𝑆′的最优解,因为如果𝑆′存在更优的解,𝐴就不是最优解了 因为𝐵是𝑆′的最优解,所以𝑖𝑘+1∈𝐵,所以最优解包含 1,𝑖2,…,𝑖𝑘,𝑖𝑘+1

7.2. 最优装载问题

0-1背包问题的特殊情况:价值都是1

证明最优性:去掉箱子1

7.3. 硬币找零问题

𝑣𝑘+𝛿=𝑝𝑣𝑘−1𝑤𝑘+𝐺(𝛿)≤𝑝𝑤𝑘−1

即使输入是 𝑣𝑘−1 的整数倍时,选择 𝑣𝑘 仍然要是最优的


8. Applications of Greedy Algoritnm

8.1. 哈夫曼算法证明

  1. 最小的两个𝑥,𝑦在一起
  2. 在一起之后可以等效成一个𝑧=𝑥+𝑦

8.2. 最小生成树

Prime 算法
初始化𝑆={1},不断选择 𝑆 和 𝑉−𝑆之间的最短的边,将对应的点加入到𝑆
证明: 如果𝑆和𝑉−𝑆之间的边是另一个,则和这一个构成环,可以用更小的这一个替代那一个
Kruskal 算法
排序所有边,从小到大选择不构成环的边
证明: 最小的边一定正确,合并两个端点,化归成𝑘−1问题

单链聚类是和Kruskal算法同样的流程,但是剩下𝑘个类就停了