算法设计与分析课设文档格式.docx
《算法设计与分析课设文档格式.docx》由会员分享,可在线阅读,更多相关《算法设计与分析课设文档格式.docx(21页珍藏版)》请在冰豆网上搜索。
20世纪80年代末,研究者们开始尝试采用启发式算法求解最大团问题,提出了各种各样的启发式算法,如顺序贪婪启发式算法、遗传算法、模拟退火算法、禁忌搜索算法、神经网络算法等,并且取得了令人满意的效果。
在时间上,由于采用了启发式信息,启发式算法的运算时间与确定性算法的运算时间之间的比值会随着图的顶点、边密度的增加而变得越来越小。
唯一的缺点就是不一定能找到最优值,有时只能找到近优值。
近年来研究表明,单独使用一种启发式算法求解最大团问题,算法性能往往并不是很好,因此,常借鉴算法之间优势互补策略,形成新的混合启发式算法来求解最大团问题。
当前求解该问题最好的启发式算法有反作用禁忌搜索(ReactiveTabuSearch,RTS)算法、基于遗传算法的简单启发式算法(SimpleHeuristicBasedGeneticAlgorithm,HGA)、DLS-MC算法等。
3.求解最大团问题的常用算法
回溯法和分支限界法。
3.1顺序贪婪启发式算法
顺序贪婪启发式算法是最早的求解最大团的启发式算法。
这类算法通过给一个团重复进行加点操作得到一个极大团或者对一组并不是团的子图重复进行删除顶点操作以得到一个团。
1987年,Kopf和Ruhe把这类型算法分为Bestin和Worstout两类。
(1)Bestin方法的基本思路:
由一个团出发,和这个团中顶点相连的顶点组成候选集;
然后以一定的启发式信息,从中选择顶点加入团中,以后反复进行,直到最后得到一个极大团。
(2)Worstout方法的基本思路:
从整个顶点集开始,然后按一定的启发式信息,从中反复进行删除顶点操作,直到最后得到一个团。
顺序贪婪启发式算法有很大不足,该算法一旦找见一个极大团,搜索就停止,因此找到最大团的概率相对较低。
3.2局部搜索启发式算法
假设SG为图的所有极大团的集合,由于顺序贪婪启发式算法仅能找见
SG中的一个极大团,因此,为了提高解的质量,应当扩大在
SG的搜索区域,比如,可以在极大团S的邻居中继续进行搜索,以扩大搜索区域,进而提高解的质量。
在局部搜索启发式算法中,如果搜索S的邻居越多,提高解的质量的机会就越大。
依赖不同的邻居定义,局部搜索启发式算法可以得到不同的解。
在局部搜索启发式算法中,比较有名的算法是K-interchange启发式算法,它是一种基于K-neighbor邻居实现的,在解集S的K邻居中进行局部搜索的方法。
分析可知,局部搜索启发式算法存在一个问题,即仅能够找见一个局部最优值。
所以为了提高求解的质量,常把该算法和其它算法相混合,从而得到求解最大团问题的新的算法。
WaynePullan和HolgerH.Hoos基于这一思想提出了求解最大团问题的DLS-MC算法,该算法是plateausearch局部搜索启发式和算法迭代改善法相混合得到的,算法性能非常好,在该方法中引入了顶点惩罚函数,该函数在算法的求解过程中能够动态改变;
在算法执行过程中迭代改善法和plateausearch算法轮流执行来提高解的质量。
在基准图上对该算法进行了测试,性能非常好。
3.3智能搜索启发式算法
智能搜索算法主要有遗传算法、禁忌算法、模拟退火算法等。
3.3.1遗传算法
遗传算法(GeneticAlgorithm,GA)是一种基于自然选择和群体遗传机理的搜索算法,它模拟了自然选择和自然遗传过程中发生的复制、交叉和变异现象。
1993年,Carter和Park首次提出使用遗传算法求解最大团问题,但由于所求解的质量差,计算复杂度高,因此,他们认为遗传算法并不适合求解最大团问题。
与此同时,Bä
ck和Khuri致力于最大独立集问题的求解,却得到了完全相反的结论,通过选用合适的适应度函数,取得了很好的效果。
因此在使用GA来解决最大团问题时,适应度函数起着非常关键的作用。
此后,基于遗传算法求解最大团问题的方法逐渐增多,但在提高解的质量,降低算法复杂度上方面却没有大幅度的提高。
l998年,Marchiori提出了一种基于遗传算法的简单启发式算法(simpleheuristicbasedgeneticalgorithm,HGA)。
算法由两部分组成:
简单遗传算法(simplegeneticalgorithm,SGA)和简单的贪婪启发式局部搜索算法(simplegreedyheuristiclocalsearchalgorithm,SGHLSA)。
在基准图上对算法HGA的性能进行测试,证明了该算法在解的质量和计算速度方面都优于基于遗传算法的其它算法。
因此,单纯使用遗传算法(改动变异、杂交、选择等算子)求解最大团问题时,算法的性能是比较差;
要提高算法性能,遗传算法最好能和局部搜索算法相结合。
3.3.2模拟退火算法
模拟退火(SimulatedAnnealing,SA)算法是N.Metropolis在1953年提出的一种基于物质退火过程的随机搜索算法,是一种迭代求解的启发式随机搜索算法。
首先在高温下较快地进行搜索,使系统进入“热平衡”状态,大致地找到系统的低能区域。
随着温度的逐渐降低,搜索精度不断提高,可逐渐准确地找到最低能量的基态。
作为局部搜索算法的扩展,当邻域的一次操作使当前解的质量提高时,接受这个改进解作为新的当前解;
反之,以一定的概率接受相对质量比较差的解作为新的当前解。
Aarts和Korst提出使用模拟退火算法来解决独立集问题,建议在算法设计时引入惩罚函数,但却没有提供任何的实验结果。
问题的解空间S是图G的全部可能的子图,并不要求是独立集,对于任一子图G*,成本函数为f(V'
)=|V'
|-|E'
|,其中V'
是图G*的顶点集,E'
是图G*的边集,是权因子(>
1)。
选择邻居时,费用值大的将被选中,因此求解最大独立集问题也就是最大化成本函数问题。
Homer和Peinado把模拟退火算法和Johnson的贪婪启发式算法、Boppan的随机化算法、Halldorsson的子图排除法3种启发式算法进行比较,结果比这3种算法要好很多。
总之,模拟退火算法在处理最大团问题上是一个非常好的算法。
3.4改进蚁群算法-Ant最大团
蚁群算法是由DorigoM.等人依据模仿真实的蚁群行为而提出的一种模拟进化算法。
蚂蚁之间是通过一种称为信息素(Pheromone)的物质传递信息的,蚂蚁能够在经过的路径上留下该种物质,而且能够感知这种物质的存在及其强度,并以此来指导自己的运动方向。
因此,由大量蚂蚁组成的集体行为便表现出一种信息正反馈现象:
某一条路径上走过的蚂蚁越多,该路径上留下的信息素就越多,则后来者选择该路径的概率就越大。
蚂蚁之间就是通过这种信息素的交流,搜索到一条从蚁巢到食物源的最短路径。
2003年,Fenet和Solnon提出了求解最大团问题的蚁群算法AntClique,该算法仍然将信息素留在边上,信息素ij是指把结点i和结点j分配到同一个团中的期望。
由于没有使用局部启发信息,这使得迭代初期各候选顶点的选择概率几乎相等,这样算法在迭代初期有一定的盲目性,往往需要更多的迭代次数才能得到最优解。
针对这些不足及最大团问题的特点,曾艳于2010年提出了改进的蚁群算法-Ant最大团。
算法伪代码描述如下:
ProcedureVertex_AntClique
Initialize//初始化信息素和局部启发信息
Repeat
Forkin1...nbAntsdo:
Chooserandomlyafirstvertexvf
V
Ck{vf}
Candidate{vi|(vf,vi)
E}
WhileCandidate≠0do
Chooseavertexvi
Candidatewithprobabilityp(vi);
CkCk∪{vi}
CandidateCandidate∩{vj|(vi,vj)
Endofwhile
Endoffor
Updatepheromonew.r.t{C1,…,CnbAnts}
Untilmaxcyclesreachedoroptimalsolutionfound
Endofprocedure
在Ant最大团中,增加了局部启发信息;
信息素和启发信息不是留在边上,而是留在顶点上。
这样,变量和由二维降为一维,既可节省存储空间,又可提高运行速度,大量实验表明,该算法运算速度更快,效率更高。
3.6回溯法
3.6.1算法基本思想
回溯法(BacktrackingAlgorithm,BA)有“通用的解题法”之称,用它可以系统地搜索一个问题的所有解或任一解,是一个既带有系统性又带有跳跃性的搜索算法。
在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。
算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解,如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯;
否则,进入该子树,继续按照深度优先的策略进行搜索。
BA在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。
而BA在用来求问题的任一解时,只要搜索到问题的一个解即可结束。
这种以深度优先的方式系统地搜索问题的解的算法称为回溯法,它适用于解一些组合数较大的问题。
回溯法搜索解空间树时,根节点首先成为一个活结点,同时也成为当前的扩展节点。
在当前扩展节点处,搜索向纵深方向移至一个新节点。
这个新节点就成为一个新的活结点,并成为当前扩展节点。
如果当前扩展节点不能再向纵深方向移动,则当前的扩展节点就成为死结点。
此时,往回回溯至最近的一个活节点处,并使这个活结点成为当前的扩展节点。
回溯法以这种方式递归地在解空间中搜索,直至找到所有要求的解或解空间已无活结点为止。
3.6.2算法设计思想
搜索:
回溯法从根结点出发,按深度优先策略遍历解空间树,搜索满足约束条件的解。
剪枝:
在搜索至树中任一结点时,先判断该结点对应的部分解是否满足约束条件,或者是否超出目标函数的界;
也即判断该结点是否包含问题的解,如果肯定不包含,则跳过对以该结点为根的子树的搜索,即剪枝(Pruning);
否则,进入以该结点为根的子树,继续按照深度优先的策略搜索。
一般来讲,回溯法求解问题的基本步骤如下:
(1)针对所给问题,定义问题的解空间;
(2)确定易于搜索的解空间结构;
(3)以深度优先方式搜索解空间,并在搜索过程中利用Pruning函数剪去无效的搜索。
无向图G的最大团问题可以看作是图G的顶点集V的子集选取问题。
因此可以用子集树表示问题的解空间。
设当前扩展节点Z位于解空间树的第i层。
在进入左子树前,必须确认从顶点i到已入选的顶点集中每一个顶点都有边相连。
在进入右子树之前,必须确认还有足够多的可选择顶点使得算法有可能在右子树中找到更大的团。
用邻接矩阵表示图G,n为G的顶点数,cn存储当前团的顶点数,bestn存储最大团的顶点数。
cn+n-i为进入右子树的上界函数,当cn+n-i<
bestn时,不能在右子树中找到更大的团,利用剪枝函数可将Z的右结点剪去。
3.6.3实例分析
如图1所示,给定无向图G={V,E},其中V={1,2,3,4,5},E={(1,2),(1,4),(1,5),(2,3),(2,5),(3,5),(4,5)}。
根据最大团定义,子集{1,2}是图G的一个大小为2的完全子图,但不是一个团,因为它包含于G的更大的完全子图{1,2,5}之中。
{1,2,5}是G的一个最大团。
{1,4,5}和{2,3,5}也是G的最大团。
图2是无向图G的补图G'
。
根据最大独立集定义,{2,4}是G的一个空子图,同时也是G的一个最大独立集。
虽然{1,2}也是G'
的空子图,但它不是G'
的独立集,因为它包含在G'
的空子图{1,2,5}中。
{1,2,5}是G'
的最大独立集。
{1,4,5}和{2,3,5}也是G'
图1无向图G图2无向图G的补图G'
以图1为例,利用回溯法搜索其空间树,具体搜索过程如下:
假设我们按照12345的顺序深度搜索。
开始时,根结点R是唯一活结点,也是当前扩展结点,位于第1层,此时当前团的顶点数cn=0,最大团的顶点数bestn=0。
在这个扩展结点处,我们假定R和第二层的顶点1之间有边相连,则沿纵深方向移至顶点1处。
此时结点R和顶点1都是活结点,顶点1成为当前的扩展结点。
此时当前团的顶点数cn=1,最大团的顶点数bestn=0。
继续深度搜索至第3层顶点2处,此时顶点1和2有边相连,都是活结点,顶点2成为当前扩展结点。
此时当前团的顶点数cn=2,最大团的顶点数bestn=0。
再深度搜索至第4层顶点3处,由于顶点3和2有边相连但与顶点1无边相连,则利用剪枝函数剪去该枝,此时由于cn+n-i=2+5-4=3>
bestn=0,则回溯到结点2处进入右子树,开始搜索。
再深度搜索至第5层顶点4处,由于顶点3和4无边相连,剪去该枝,回溯到结点3处进入右子树,此时当前团的顶点数cn=2,最大团的顶点数bestn=0。
继续深度搜索至第6层顶点5处,由于顶点5和4有边相连,且与顶点1和2都有边相连,则进入左子树搜索。
由于结点5是一个叶结点,故我们得到一个可行解,此时当前团的顶点数cn=3,最大团的顶点数bestn=3。
vi的取值由顶点1至顶点5所唯一确定,即v=(1,2,5)。
此时顶点5已不能再纵深扩展,成为死结点,我们返回到结点4处。
由于此时cn+n-i=3+5-6=2<
bestn=3,不能在右子树中找到更大的团,利用剪枝函数可将结点4的右结点剪去。
以此回溯,直至根结点R再次成为当前的扩展结点,沿着右子树的纵深方向移动,直至遍历整个解空间。
最后得到图1的按照12345的顺序深度搜索的最大团为U={1,2,5}。
当然{1,4,5}和{2,3,5}也是其最大团。
3.6.4程序设计及测试
3.6.4.1程序代码
//MaxClique.cpp:
定义控制台应用程序的入口点。
/*
回溯法求解最大团问题
*/
#include"
stdafx.h"
#include<
fstream>
iostream>
stdlib.h>
conio.h>
usingnamespacestd;
#defineMAX_v50//定义一个最大顶点个数
typedefstruct{
inta[MAX_v][MAX_v];
//无向图G的邻接矩阵
intv;
//无向图G的顶点
inte;
//无向图G的边
intx[50];
//顶点与当前团的连接,x[i]=1表示有连接
intbestx[50];
//当前最优解
intcn;
//当前团的顶点数目
intbestn;
//最大团的顶点数目
}最大团;
voidCreat(最大团&
G);
voidBacktrack(最大团&
G,inti);
G){
inti,j;
ifstreamfin("
data.txt"
);
if(!
fin)
{
cout<
<
"
不能打开文件:
endl;
exit
(1);
}
fin>
>
G.v;
for(inti=1;
i<
=G.v;
i++)
for(intj=1;
j<
j++)
fin>
G.a[i][j];
for(i=1;
i++)//初始化
G.bestx[i]=0;
G.x[i]=0;
G.bestn=0;
G.cn=0;
cout<
————————————————"
———回溯法求解最大团问题———"
输入初始化无向图矩阵为:
//初始化
for(j=1;
G.a[i][j]<
"
;
}
G,inti){
if(i>
G.v){
for(intj=1;
j<
j++)
G.bestx[j]=G.x[j];
G.bestn=G.cn;
return;
}
//检查顶点i与当前团的连接
intOK=1;
for(intj=1;
=i;
j++)
if(G.x[j]&
&
G.a[i][j]==0){
//i不与j相连
OK=0;
break;
if(OK){
G.x[i]=1;
//把i加入团
G.cn++;
Backtrack(G,i+1);
G.cn--;
if(G.cn+G.v-i>
G.bestn){
G.x[i]=0;
Backtrack(G,i+1);
voidmain(){
最大团G;
Creat(G);
Backtrack(G,1);
最大团包含的顶点数为:
G.bestn<
最大团方案为:
("
if(G.bestx[i]==1){
cout<
}
)"
getch();
3.7分支限界法
3.7.1算法描述
分支限界(BrandandBound)法类似于回溯法,也是一种在问题的解空间树上搜索问题的解的算法。
分支限界法常以广度优先或最小耗费(最大效益)优先的方式搜索解空间树。
在分支限界法中,每一个活结点只有一次机会成为扩展结点。
一旦活结点成为扩展结点,就一次性产生其所有儿子结点。
在这些儿子结点中,舍弃那些导致不可行解或导致非最优解的儿子结点,将其余儿子结点加入活结点表中。
此后,从活结点表中取下一个结点成为当前扩展结点,并重复上述结点扩展过程,直至找到所需的解或活结点表为空时为止。
具体来讲,分支限界法由“分支”策略和“限界”策略两部分组成。
“分支”体现在对问题空间是按广度优先搜索;
“限界”策略是为了加速搜索速度而采用启发式剪枝的策略。
分支搜索法采用广度优先的策略,依次生成E结点所有分支(即所有的子结点)。
在生成的结点中,将采用更有效的约束函数(限界函数)控制搜索路径,去除那些不满足约束条件(即不可能导出最优解)的结点,使之能更好地朝着状态空间树上有最优解的分支推进。
根据从活结点表中选择下一个扩展结点的方式的不同,分支限界法主要分为以下两类:
(1)队列式(FIFO)分支限界法
队列式分支限界法将活结点表组织成一个队列,并按队列的FIFO原则选取下一个结点成为当前扩展结点。
具体流程为:
A.初始化,根结点是唯一的活结点,根结点入队。
B.从活结点队中取出根结点后,作为当前E结点。
对当前E结点,先从左到右地产生它的所有儿子,用约束条件检查,把所有满足约束函数的儿子加入活结点队列中。
C.重复上述过程:
再从活结点表中取出队首结点(队中最先进来的结点)为当前E结点,……;
直到找到一个解或活结点队列为空为止。
(2)优先队列式分支限界法
优先队列式分支限界法将活结点表组织成一个优先队列,并按优先队列中规定的结点优先级选取优先级最高的下一个结点成为当前扩展结点。
对每一活结点计算一个优先级,并根据这些优先级,从当前活结点表中优先选择一个优先级最高(最有利)的结点作为扩展结点,使搜索朝着解空间树上有最优解的分支推进,以便尽快地找出一个最优解。
3.7.2算法求解流程
(1)确定一个合理的限界函数,并根据限界函数确定目标函数的界。
(2)按广度优先策略遍历问题的解空间树:
A.在当前E结点上,依次搜索该结点的所有儿子结点,分别估算这些孩子结点的目标函数的可能取值。
B.如果某儿子结点的目标函数可能取得的值超出目标函数的界,则将其丢弃;
否则,将其加入活结点表中。
C.再依次从活表中选取使目标函数的值取得极值的结点成为当前E结点。
(3)重复上述过程,直到找到最优解。
3.7.3优先队列式分支限界法求解最大团问题
(1)上界函数
用变量cliqueSize表示与该结点相应的团的顶点数;
level表示结点在子集空间树中所处的层次;
用cliqueSize+n-level+1作为顶点数上界upperSize的值。
在此优先队列式分支限界法中,upperSize实际上也是优先队列中元素的优先级。
算法总是从活结点优先队列中抽取具有最大upperSize值的元素作为下一个扩展元素。
(2)核心算法设计
子集树的根结点是初始扩展结点,对于这个特殊的扩展结点,其cliqueSize的值为0。
算法在扩展内部结点时,首先考察其左儿子结点。
在左儿子结点处,将顶点i加入到当前团中,并检查该顶点与当前团中其他顶点之间是否有边相连。
当顶点i与当前团中所有顶点之间都有边相连,则相应的左儿子结点是可行结点,将它加入到子集树中并插入活结点优先队列,否则就不是可行结点。
接着继续考察当前扩展结点的右儿子结点。
当upperSize>
bestn时,右子树