算法设计与分析


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𝑛𝑥的值(选择12𝑛次根),求出𝐴(𝑥𝑗)𝐵(𝑥𝑗)的值,再用求出𝐶(𝑥𝑗)=𝐴(𝑥𝑗)𝐵(𝑥𝑗),最后多项式插值求出𝐶(𝑥)

  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算法同样的流程,但是剩下𝑘个类就停了