传统算法总结
经典的传统算法可以分为两类:
①仅评估完整解的算法
②需要部分构造解的算法
1枚举法
①求解SAT
思路:
产生所有长度为n的二进制串,从(0……000)到(1……111)共有2n个。枚举时将每个二进制串对应一个整数,每次给该整数加1,对每次产生新的二进制串对其进行评估:如果该串满足符合布尔表达式,其值为1,否则为0。
改进:
可以采用回溯的方法减少实际要搜索的串:将解空间根据变量x1的取值分成两个不相交的子空间。然后根据x2的取值将每个空间再分成两个不相交的子空间……,依此类推,直到第n个变量。这样将解空间组织成了树状结构,即可以使用深度优先的搜索算法。
推广:
F 求解SAT问题实质上是要生成n个二进制数字组成的所有2n个字符串。即生成所有的n元组(a1,a2,……an)。
F 该问题可以等价于求给定集合{x1,x2,……xn}的所有子集问题。因为可以假设二进制字符串中第j位为1,表示xj位于子集中。
F 该问题还可以推广到其他种类的n元组。例如每个aj是十进制数字。每个aj还可以定义不同的上限mj。
算法:
F 可以简单地通过控制逐位加一来实现。
F 当n比较小时,更可以简单地通过n重循环来实现。
②求解TSP
思路:
一是TSP问题可能不是全互联的,所以有些排列是非法的。
二是如何产生n个数的所有可能排列。
方法一:
生成对应于k的一个排列。
方法二:
递归法生成全排列。
方法三:
将一个排列转换成另一个排列;
并且不允许出现重复的排列;
直到生成所有的排列。
③求解NLP
可以通过采取适当的“粒度”,使得问题可以采用枚举法求解。
例如求f(x1,x2)的最大值(其中x1Î[-1,3],x2Î[0,12]),
可以将x1的取值范围分为400个区间,将x2的取值范围分为600个区间,这样就可以对这样400´600=240000个单元进行枚举算法处理了。
2局部搜索法
①求解SAT
对于某些类型的SAT问题,局部搜索算法的结果十分有效。所谓局部搜索,就是根据当前解连续反位若干个变量的值(由假变为真或者相反)。
procedure GSAT
begin
for (i=1; MAX-TRIES; i++)
t=随机产生一组真值指派;
for (j=1; MAX-FLIPS; j++)
if满足公式return t;
else进行反位操作;
return false;
end
有关GSAT的进一步讨论
②求解TSP
2-optimize算法:
从城市的一个随机排列(称作路径T)开始,并不断改善T。T的邻域定义为交换T中不相邻的两条边所得到的所有路径的集合(该变换称作2-interchange)。
有关k-optimize的进一步讨论
③求解NLP
基于局部搜索的原理求解NLP问题的方法很多,其原因是没有哪一种方法比别的方法更好。而且要想找到一个通用的求解NLP的局部搜索算法来处理所有的非线性模型是不现实的。它势必转换成穷举法。
常用的方法有二分法、线截法、不动点法和梯度法等。
3线性规划法
单纯形法只适合于求解评估函数和约束条件是线性的一类问题。如果是非线性的则必须用其他技巧补偿。特别是当评估函数潜在的存在多峰甚至是不连续的时候,则不能使用该方法。求解该问题的关键方法是减小搜索的空间。
4分治法
将一个看似复杂的问题分解成若干个相对简单的问题,逐个地求解这些简单的问题,然后再找一种方法将各部分的解组合成完整的答案。
应用该方法应保证将问题分解再将其结果组合所花费的工作量比直接求解代价小时才有效。
基本算法框架:
procedure D&C(P)
begin
将问题P分解成子问题P1,P2,……Pk;
for(i=1; i<=k; i++)
if (size(Pi))<p 求解Pi;得到Si;
else Si=D&C(Pi);
将Si组合到最终解中;
end;
5贪心法
通过一系列步骤来构造完整解的。与分治法不同之处在于其基本思想是每一步都选择最佳的步骤,以期获得最大的好处。
实例:Dijkstra算法:求某一点到其余各点的最短路径。
思路:在分治的基础上,根据特定的标准,将n个分量排序,按序逐个加入。
关键:设计特定的标准。按路径长度,逐个生成最短路。
时间效率:O(n2)
思考题:如何在输出最短路径长度的同时,输出相应的最短路径?
问题:
对于有些问题不是十分有效,因为每一步作出的最佳决定并不一定能保证得到全局的最优解。
❤求解SAT问题:
思路:
对于从1到n的每个变量,不分次序地指定一个真值指派(TRUE或是FALSE),使之尽可能地满足最大数目的当前未满足的子句。
这种无节制的贪心其效率可能是很低的。例如:
如果考虑变量x1,则选择x1=TRUE,因为这样可以满足三个子句,但实际上当x1=TRUE时第一个子句不能满足,只要x1为TRUE,无论x2、x3、x4取什么值整个公式都不可能为真。
改进一:
先考虑出现次数少的变量,再考虑经常出现的变量。
改进二:
考虑子句的长度,使在短子句和长子句中出现的变量具有不同的重要性。
6动态规划法
基本原理是在求解问题的过程中,通过递归地处理位于当前位置和所达目标之间的中间点来寻找整个问题的解。
适用问题具有下述特点:
- 整个问题的求解可划分为若干阶段的一系列决策过程;
- 每个阶段有若干可能状态;
- 每个阶段的决策是相对独立的;
- 各阶段之间的转换有明确定义的费用;
- 选择最佳决策时有递推关系。
最直观的例子是Harvey Wagner提出的公共马车问题(stagecoach problem),即多段图求两点间最短路径问题。
实例1:Floyed算法
时间效率:O(n3)
实例2:矩阵乘法
实例3:5城市的非对称TSP
则递推关系式为:
7回溯法
需要定义一个限界函数,在求解问题过程当中,一方面要不断地修改限界函数,另一方面要不断地用限界函数去测试正在构造的解向量的部分向量(x1,x2……xi),看其是否可能导致最优解,如果判断出不能导致最优解,则取消对其余的部分向量(xi+1,xi+2……xn)的计算。
实例:八皇后问题
该问题可以推广到n个皇后的情况。n皇后问题可以表示成n元组(x1,x2……xn),其中xi表示第i个皇后所在的列号。该问题的完整的解空间是nn个n元组。因约束条件是不能有两个xi相同,即所有的解必须是(1,2…n)的排列。所以其解空间减少到n!个n元组。
四皇后问题搜索树
四皇后问题回溯过程
四皇后问题回溯法搜索树
8分枝定界法
思路:
通过限界函数去掉那部分不可能找到最优解的搜索空间,即删除那些还没有完全生成后继结点的没有希望的活动结点。与回溯法不同的是,不是采用深度优先的搜索方法,而是生成当前结点的所有后继结点后,再生成其它活动结点的后继结点。并且同样在生成结点的过程中用限界函数删除死结点。
改进:
在上述分枝定界法中,选择当前结点的策略非常死板,可以说是完全盲目的。改进的分枝定界算法对所有的活结点排序,然后按序选取下一个当前结点。而要对所有活动结点排序,就需要附加相应的计算,可以通过设置一个成本函数c(x),对所有活动结点x进行评估。
成本函数的定义如下:
F 如果x是答案结点,则c(x)等于搜索树中根结点到结点x的成本(比如计算的复杂度);
F 如果x不是答案结点,且x子树中不包含答案结点,则c(x)=¥;
F 否则,c(x)等于生成答案结点之前,x子树需生成的结点个数。
LC分支定界法:
成本函数c(x)的计算量与求解原问题具有相同的复杂度。所以要得到精确的成本函数一般是不现实的。一般只是设置成本函数的评估函数。使用成本评估函数来选取下一个当前结点的分支界限法称作最小成本分支定界法,或称作LC(Lease Cost)分支定界法。
成本评估函数一般是由上界函数u(x)和下界函数d(x)组成。
上界函数u(x)的作用是在搜索过程中决定是否剪枝,即是否跳过该结点。下界函数d(x)的作用是在搜索过程中决定是否优先搜索以该结点为根的子树。
成本评估函数,即上、下界函数的设计和计算是LC分支定界法的关键。
实例:5城市TSP
归约矩阵的定义:
- 如果矩阵的某一行(列)至少包含一个0,且其余元素均非负,则该行(列)称为已归约的行(列)。
- 如果矩阵的所有行和列都是已归约的行和列,则称为归约矩阵。
归约矩阵法计算下界基本思想:
- 将权矩阵W变换为同阶的归约矩阵W',则它们所对应的最小代价路径不变。
- 将权矩阵的第i行减去同一正数ri,对应的最小代价路径不变。
- 将权矩阵的第j列减去同一正数r‘j,对应的最小代价路径不变。
归约矩阵法计算下界的基本步骤:
- 对W的每一行减去同一正数ri(i=1,2,……n),在保证权值非负的条件下使ri尽量大。
- 对(1)归约后的矩阵的每一列减去同一正数r’j(j=1,2,……n),在保证权值非负的条件下使r’j尽量大。
所以,分支定界技术是一种设计快速算法的有效技术。它通过活动结点表和评估函数来选择搜索顺序,使得算法具有一定的智能,减少了搜索的盲目性。有利于更快地找到最优路线。
分支定界法的改进:
- 状态空间树
- 评估函数的设计和计算
状态空间树:
整个搜索空间S可以按照是否包含边(1,2)进行划分,类似的再进一步按照是否包含边(2,3)进行划分,……以此一直进行下去。下面是按照这一原理生成的树状搜索空间。叶结点表示了(5-1)!/2=12种可能的路径。
评估函数的设计和计算:
因为在TSP完整解中,每个城市都有两条邻接边,一条是进入这个城市。另一条是离开这个城市到下一个城市。所以将每个城市的最短的两条邻接边的长度相加再除以2就是一个下界。
一旦某条路径的一些边被指定后,就可以根据这些信息计算部分解的新的下界。
在搜索过程中,还可以通过包含隐含边或者删除不可能边来不断改善下界值。
下界选得越好,算法搜索的就越快。但是需要注意的是计算这些下界也需要有部分开销。
分枝定界法的局限:
F 不能从根本上改进问题的时间复杂度。
F 比回溯法需要更多的空间。
9 A*算法
基于最佳优先(Best First)搜索的A*算法,通过设置一个更有效的评估函数,提供尽可能多的信息来适应比较复杂的多极值问题。
评估函数一般由两部分构成:
eval(x)=c(x)+h(x)
其中c(x)是x决策已经花费的代价,h(x)是x决策未来可能花费的代价。
评估函数更一般的形式为:
eval(x)=c(x)+k×h(x)
A*算法的定义:
如果采用函数eval(依非降次序)来排列备选结点,则这样的搜索算法称作A算法。
如果h满足对任意的n∈N,有h(n)<=h*(n),则这样的搜索算法称作A*算法。
A*算法的控制方法:
爬山法
每搜索到一个结点后,在其所有子结点中,按估计函数选择最优者。犹如瞎子爬山,总是朝最陡的方向爬。
最佳法
是一种全局择优搜索法:在保留的所有已生成但未扩展的结点中,根据估计函数对它们进行估计,从中选出最佳的结点扩展。
实例:九宫问题
改进:eval(x)=d(x)+w(x)。
其中:w(x)表示错位的数字的个数;
d(x)为结点x的搜索深度。
更好的启发函数:
h(x)=p(x)+3×s(x)
其中的s(x)的计算方法是:若中心有数则加1;若周围的数依顺时针方向有一个数与其后面的数为逆序则加2。
启发函数启发能力的比较:
h1=0
h2(x)=w(x) 错位的数字个数
h3(x)=p(x) 每个数字到目标的距离之和
h4(x)=p(x)+3×s(x)中心有数加1,次序颠倒加2
显然,对于任意结点x,有:
h4(x)³h3(x)³h2(x)³h1(x)
启发函数h4的能力优于h3,h3优于h2,h2优于h1。