算法笔记(一)-概论

本文是算法笔记系列的开端,内容主要参考卜东波老师授课内容、算法导论学习整理及平日的一些积累
查看原文,点我

概述

无论是生活还是科研、工作中遇到的问题,通常可以采用下述的过程去解决

  • 找到需要解决的实际问题
  • 将实际问题形式化为数学形式
  • 找到求解问题的算法
这里很重要的两部分是:将实际问题形式化为数学形式和设计求解问题的算法。

这个系列的笔记主要是关注找到求解问题的算法,下面是一个求解问题的思路树:

通常所需要解决的问题可以依据求解算法分为两类:

  • 组合优化可以求解的问题,这类问题有着悠久的历史,经典的TSP、传统的排序算法都是属于这一类算法。为了描述方便,这类问题在文章中简记为C.U.问题(combination and optimization)
  • 统计学习可以求解的问题,这类问题通常可以用机器学习、模式识别这样的方式求解。为了描述方便,这类问题在文章中简记为STAT问题

这样的划分可以让我们面对一个全新的问题时不至于手足无措,无法下手,也能使求解问题时思路更加清晰。当然这样的分类方式并非是严格意义上的划分,现在同样有以ML/DL的方式求解传统算法的尝试。

C.U.问题

解决这类问题,有一种很“套路”的技巧,可以尝试以下列三种角度分析问题

  • 观察问题是否可以分解,可以分解就尝试采用归纳(induction)的策略
  • 问题不能分解,观察问题的解空间,是否可以尝试逐步迭代求解(improvement)
  • 观察问题解的形式,可以发现问题是否具有枚举的特性,如果具备这样的性质,可以尝试采用枚举(enumeration)的思想求解问题

对于以上述三种角度,都无法观察到我们想要的特性,这类问题记为Hard problem,我们需要尝试其他的策略,比如松弛:既然直接求得最优解是一件比较困难的事,那我们尝试放缩到求近似最优解。

在下文会对三种角度分析问题做一些简要介绍,内容比较抽象,可以结合系列中国后续文章对算法的分析过程

问题是否可以分解

实际上,这里用“分解”描述并不准确,更为准确的描述是“reduce to smaller subproblems”:分解容易误解为将原题目划分为多个“独立”的子问题,而实际上并非如此;“reduce to smaller subproblems”,这些“smaller subproblems”往往是存在联系的。

介绍到这里会比较抽象,我们结合实例来介绍reduce的技巧

有两个整数a,b(a,b不同时为0),求解a和b最大公约数
这个问题本身很简单,我们尝试用上文已经介绍的方式来分析该问题

  1. Formulation:

Input: 两个n位数a和b(a>=b)
Output: gcd(a,b)

  1. 分析过程

a和b非常抽象,我们给定其具体的值来观察分析,求解gcd(101,1949)
对例子进行观察,可以得出
gcd(101,1949)=gcd(101,1949 mod 101)=gcd(101,30)
这样就将原问题reduce to a smaller problem,由此可以得到下述reduce的过程
gcd(101,1949)=gcd(101,1949 mod 101)=gcd(101,30)
gcd(101,30)=gcd(101 mod 30,30)=gcd(11,30)
gcd(11,30)=gcd(11,30 mod 11)=gcd(11,8)
gcd(11,8)=gcd(11 mod 8,8)=gcd(3,8)
gcd(3,8)=gcd(3,8 mod 3)=gcd(3,2)
gcd(3,2)=gcd(3 mod 2,2)=gcd(1,2)
gcd(1,2)=gcd(1,2 mod 1)=gcd(1,1)
gcd(1,1)=gcd(1,1 mod 1)=gcd(1,0)=1
进而可以得出求解问题的算法

这个算法实际上就是我们所熟悉的辗转相除法

求解最大公约数的辗转相除法的分析过程是“reduce to smaller problems”技巧的简单运用,实际上我们遇到的问题会更为复杂,这时应用reduce的技巧会面临两个问题:

  1. 如何判断一个问题是否可以reduce?

关键在于reduce得到的子问题是否易解,子问题的解是否容易合并为原问题的解。reduce可以用一个很朴素方式的判断:

  • 问题规模为n时不易解
  • 先考虑最简单的情况,当n=1时
  • 再考虑当n=2时
  • ⋯ \cdots

最后是否可以通过这样的过程归纳出规模为n问题的求解算法。
这里有一个经验之谈,遇到问题的input是以下的形式时,往往可以reduce:

  • 数组
    • n维数组分解为两个n/2的数组
    • 考虑小规模问题:n=1, n=2, ⋯ \cdots
  • 矩阵
    n × m n\times m n×m的矩阵,四等分,得到4个 n 2 × m 2 \frac{n}{2}\times \frac{m}{2} 2n×2m的小矩阵

  • 树的子树往往就代表着子问题
  1. 问题可以reduce,如何得到原问题的解?
  • 若问题可以reduce,则可以用分治求解
  • 若问题可以reduce、具有最优子结构,则可以尝试用动态规划求解
  • 若问题可以reduce、具有最优子结构、满足贪心选择的性质,可以尝试用贪心求解
    贪心选择性质:可以由局部最优解得到全局最优解,即在算法的每一步都可以容易地得到局部最优解,由于具有最优子结构的性质,则最后得到的解一定是全局最优的

在观察问题是否可以reduce并尝试求解的过程中,蕴含了归纳(induction)的思想:

  • 从“smaller subproblems"中归纳出原问题的求解算法
  • 算法的正确性往往可以通过数学归纳法证明

观察问题的解空间

首先介绍下解空间的概念:问题的所有complete solution构成了问题的解空间,我们可以把解空间看做图,图中:

  • 节点:一个节点对于问题的一个complete solution
  • 边:两个节点之间存在边,代表这两个节点是邻居(neighboors),即可以通过“简单”操作从一个complete solution转换到另一个complete solution。如果两个solution在解空间中是邻居关系,则通常这两个解的差别并不会太大。

若问题不能reduce,我们又能比较“容易”的得到问题的解空间,则可以选定一个初始解,逐步迭代,每一步得到一个更好的解,最终得到想要的最优解,这里蕴含着improvement的思想

如果学过线性规划的单纯型算法,则可以做一个类比:

解空间中的complete solution相当于线性规划中的可行解,从一个可行解出发,通过转轴操作得到另一个可行解,则这两个可行解存在邻居关系,整个单纯型算法则是找到一个初始可行解s,然后通过转轴操作得到另一个可行解s’(s’是s的邻居),直至找到最优解(满足KKT条件)

这里给出一个简单的实例来描述上述过程

下图中每个节点代表一个城市,边代表两个城市之间是可达的,边的权值代表城市之间的距离,一个商人需要从城市a出发,经过所有城市再回到a,要求每个城市均要路过且仅路过一次,希望得到商人需要走的最短距离

  • 给定初始解是s:a->b->c->d->e->a (路程:25)
  • 这时开始寻找初始解的邻居,上面已经介绍,邻居与现有解的差异应该尽可能小(即需要转到的解与现有解的差异应当很小,原因会在线性规划部分进行详述);定义解之间不同边的个数为差异程度d,显然由于条件限制,d最小为2
  • 由此可以得到一个基于s improve的解s’:a->b->c->e->d->a (路程:23)

观察解的形式

对于一个问题,若其解形式为 X = { X 1 , X 2 , ⋯   , X n } , 其 中 X i 的 取 值 有 限 X=\{X_1,X_2,\cdots,X_n\},其中X_i的取值有限 X={X1,X2,,Xn}Xi,那么我们可以将所有可能的解枚举出来,进而得到我们所需的正解。比如著名的0-1背包问题(n个物品),其解形式就可以描述为 X = { X 1 , X 2 , ⋯   , X n } X=\{X_1,X_2,\cdots,X_n\} X={X1,X2,,Xn},其中:
X n = { 1 该物品放入背包  0 e l s e X_n = \begin{cases}1 & \text{该物品放入背包 } \\0 & else\end{cases} Xn={10该物品放入背包 else

在上一小节中由给出旅行商问题,我们可以从解的形式这一角度来分析该问题:

统计学习问题

这类问题并不在算法笔记系列文章之中,可以参考其他文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值