算法的概念
算法是指给出解决问题的操作步骤之后,无论是人还是机器都可以按照步骤机械性的执行得到问题的结果。
我们在日常生活中回遇到各种的实际问题遇到之后的解决流程如下:
首先在一系列世纪问题中找到一个特定的topic,得到一个实际问题,在这个实际问题的基础之上我们可以抽象出数学问题,之后通过对该数学问题的观察,特别是对输入输出结构的观察,得到解决问题的算法。
面对一个数学问题,我们首先问自己以下几个问题。
- 这个问题的最简单版本是否可以解决,假如不可以,我们可以降低问题的难度直到简单版本可以解决;
- 问题是否可以拆分为小问题,假如可以拆分,那么采用devide and
conquer方法,如果有最优子结构,可以尝试动态规划,还有短视的策略贪心可以使用; - 可行解的形式是什么,是否采用枚举办法,枚举时需要注意剪枝;
- 问题的解是否可以通过微小扰动变为另一种形式,这种可以采用逐步改进的办法解决问题,包括线性规划、局部搜索和半退火、网络流等;
基于旅行商问题介绍三种算法设计过程
traveling salesman problem, TSP
- 输入:结点集合 V = { v 1 , . . . , v n } V=\{v_1,...,v_n\} V={v1,...,vn},以及结点之间的距离矩阵 D = ( d i , j ) ∈ R n × n D = (d_{i,j}) \in R^{n\times n} D=(di,j)∈Rn×n,其中 d i , j d_{i,j} di,j表示节点 i i i与节点 j j j之间的距离;
- 输出:最短的环游路线,即从任意节点作为出发点,经过每个节点一次且仅一次,最终返回出发点的里程最短的环游;
分而治之算法设计过程简介
我们需要观察这个问题是否可以分解成简单实例,并且简单实例的解是否可以组合出复杂实例的解。
- 第一种尝试,我们尝试减少结点数,可以看到,我们可以很容易的将五个结点的实例变为四个结点的实例,但是简单实例不太容易组合成复杂实例;
- 第二种尝试,求解一个辅助问题,计算从起始结点 s s s出发,经过中间结点集合 S S S,最终达到目的节点 x x x的最短路径,其长度计为 M ( s , S , x ) M(s,S, x) M(s,S,x);
可以看到在第二种尝试之下可以将这个问题分解为简单实例并可以合起来组成复杂实例。
以5个结点的实例为例,包含结点
a
,
b
,
c
,
d
,
e
a,b,c,d,e
a,b,c,d,e,以
a
a
a为起始节点,那么可以从
b
,
c
,
e
b,c,e
b,c,e返回起点,最短里程可以表示为
m
i
n
{
d
b
,
a
+
M
(
a
,
{
c
,
d
}
,
b
)
,
d
c
,
a
+
M
(
a
,
{
b
,
d
}
,
c
)
,
d
e
,
a
+
M
(
a
,
{
c
,
b
}
,
e
)
}
min\{d_{b,a} + M(a,\{c,d\},b), d_{c,a} + M(a,\{b,d\},c), d_{e,a} + M(a,\{c,b\},e)\}
min{db,a+M(a,{c,d},b),dc,a+M(a,{b,d},c),de,a+M(a,{c,b},e)}
这个算法可以表示为以下伪代码
计算
M
(
s
,
S
,
x
)
M(s,S,x)
M(s,S,x)的伪代码
算法的复杂度计算如下,我们需要枚举所有的结点子集
S
S
S,所以总共有
2
n
2^n
2n个子集,路线起点确定,但是终点
x
x
x有
n
n
n种可能,所以总共要
O
(
2
n
n
)
O(2^nn)
O(2nn)才能计算出
M
(
s
,
S
,
x
)
M(s,S,x)
M(s,S,x)表格的值,计算出来之后还要经过
n
n
n次比较的到最终的结果,Bellman-held-karp的复杂度为
O
(
2
n
n
2
)
O(2^n n^2)
O(2nn2).
逐步改进的算法设计流程
基本过程是从问题的一个粗糙的,质量不太高的完整可行解开始,不断改进,直到获得满意的解为止,一般性框架为。
求解过程中需要关注三个方面:
- 初始粗糙可行解的选择;
- 可行解的改进办法,也即扰动办法;
- 算法的终止条件,常见的终止条件包括当前的可行解无法进一步改进;迭代次数超过预先定义的值;当前可行解的质量超过预先定义的阈值;
智能枚举算法设计流程
通过观察解的形式来枚举
- 枚举边的各种情况,最终的解的形式是边的集合,枚举各种边是否存在的情况,在这个过程中可以进行剪枝
- 枚举点的各种情况,也就是将环游表示成 X = x 1 , x 2 , . . . , x n − 2 , x i ∈ V ( 1 ≤ i ≤ n − 2 ) X=x_1,x_2,...,x_{n-2},x_i \in V (1\leq i \leq n - 2) X=x1,x2,...,xn−2,xi∈V(1≤i≤n−2).
算法的复杂度
- 时间复杂度:算法执行过程中总的基本操作次数
- 空间复杂度:算法所使用的存储单元数目
因为仅凭复杂度在一个或几个特定实例上的时间和空间复杂度难以评估其性能,因此,常用的方式是考虑具有同等规模的所有实例
- 最坏情况时间复杂度: w o r s t − c a s e t i m e c o m p l e x i t y worst-case\ time\ complexity worst−case time complexity在所有实例上基本操作次数的最大值作为时间复杂度
- 最坏情况空间复杂度: w o r s t − c a s e s p a c e c o m p l e x i t y worst-case\ space\ complexity worst−case space complexity在所有实例上使用存储单元数目的最大值作为空间复杂度
- 还有平均情况时间复杂度和平均情况空间复杂度的概念,但是需要知道问题实例的概率分布
- 算法根据时间复杂度分为指数时间算法和多项式时间算法,时间复杂度为 O ( n log n ) O(n^{\log n}) O(nlogn)的算法是指数时间算法
大O记号
- 大O记号:考虑两个函数 f ( n ) f(n) f(n) 和 g ( n ) g(n) g(n),其定义域是正整数,值域是正实数。如果存在一个正数 c > 0 c > 0 c>0 以及 N > 0 N > 0 N>0,使得对任意的 n > N n > N n>N,总有 f ( n ) ≤ c g ( n ) f(n) ≤ cg(n) f(n)≤cg(n)成立,则记为 f ( n ) = O ( g ( n ) ) f(n) = O(g(n)) f(n)=O(g(n))。
- Ω \Omega Ω记号:考虑两个函数 f ( n ) f(n) f(n) 和 g ( n ) g(n) g(n),其定义域是正整数,值域是正实数。如果 f ( n ) = O ( g ( n ) ) f(n) = O(g(n)) f(n)=O(g(n)),则可以记为 g ( n ) = Ω ( f ( n ) ) g(n) = \Omega(f(n)) g(n)=Ω(f(n))
- Θ \Theta Θ记号:如果 f ( n ) = O ( g ( n ) ) f(n) = O(g(n)) f(n)=O(g(n)) 和 g ( n ) = O ( f ( n ) ) g(n) = O(f(n)) g(n)=O(f(n)) 同时成立,则可以记为 f ( n ) = Θ ( g ( n ) ) f(n) = \Theta(g(n)) f(n)=Θ(g(n))。