算法报告旅行商问题模板汇编Word文档下载推荐.docx
- 文档编号:21625161
- 上传时间:2023-01-31
- 格式:DOCX
- 页数:25
- 大小:204.52KB
算法报告旅行商问题模板汇编Word文档下载推荐.docx
《算法报告旅行商问题模板汇编Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《算法报告旅行商问题模板汇编Word文档下载推荐.docx(25页珍藏版)》请在冰豆网上搜索。
中各个顶点一次且仅一次,最后回到出发点i的最短路径长度,开始时,
,于是,TSP问题的动态规划函数为:
2.1.3算法分析
(1)for(i=1;
i<
N;
i++)//初始化第0列
d[i][0]=c[i][0];
(2)for(j=1;
j<
-1;
j++)
for(i=1;
n;
i++)//依次进行第i次迭代
if(子集V[j]中不包含i)
对V[j]中的每个元素k,计算V[m]==V[j]-k;
d[i][j]=min(c[i][k]+d[k][m]);
(3)对V[
-1]中的每一个元素k,计算V[m]==V[
-1]-k;
d[0][
-1]=min(c[0][k]+d[k][m]);
(4)输出最短路径长度d[0][
-1];
2.1.4时间复杂性
动态规划法求解TSP问题,把原来的时间复杂性是O(n!
)的排列问题,转化为组合问题,从而降低了算法的时间复杂性,但它仍需要指数时间。
2.2贪心法
2.2.1贪心法的设计思想
贪心法在解决问题的策略上目光短浅,只根据当前已有的信息就做出选择,而且一旦做出了选择,不管将来有什么结果,这个选择都不会改变。
换言之,贪心法并不是从整体最优考虑,它所做出的选择只是在某种意义上的局部最优。
这种局部最优选择并不总能获得整体最优解,但通常能获得近似最优解。
2.2.2最近邻点策略求解TSP问题
贪心法求解TSP问题的贪心策略是显然的,至少有两种贪心策略是合理的:
最近邻点策略和最短链接策略。
本文仅重点讨论最近邻点策略及其求解过程。
最近邻点策略:
从任意城市出发,每次在没有到过的城市中选择距离已选择的城市中最近的一个,直到经过了所有的城市,最后回到出发城市。
2.2.3算法分析
1.P={};
2.V=V-{u0};
u=u0;
//从顶点u0出发3.循环直到集合P中包含n-1条边3.1查找与顶点u邻接的最小代价边(u,v)并且v属于集合V;
3.2P=P+{(u,v)};
3.3V=V-{v};
3.4u=v;
//从顶点v出发继续求解
2.2.4时间复杂性
但需注意,用最近邻点贪心策略求解TSP问题所得的结果不一定是最优解。
当图中顶点个数较多并且各边的代价值分布比较均匀时,最近邻点策略可以给出较好的近似解,不过,这个近似解以何种程度近似于最优解,却难以保证。
2.3回溯法
2.3.1回溯法的设计思想
回溯法(探索与回溯法)是一种选优搜索法,按选优条件向前搜索,以达到目标。
但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。
若已有满足约束条件的部分解,不妨设为(x1,x2,x3,……xi),I<
n,则添加x(i+1)属于s(i+2),检查(x1,x2,……,xi,x(i+1))是否满足条件,满足了就继续添加x(i+2)、s(i+2),若所有的x(i+1)属于s(i+1)都不能得到部分解,就去掉xi,回溯到(xi,x2,……x(i-1)),添加那些未考察过的x1属于s1,看其是否满足约束条件,为此反复进行,直至得到解或证明无解。
2.3.2算法分析(回溯法+深度优先搜索策略)
因为是采用回溯法来做,肯定是递归,然后还需要现场清理。
要设置一个二维数组来标识矩阵内容,然后回溯还需要设计
一个二维标记数组来剪枝,设定一个目标变量,初始为无穷大,
后续如果有比目标变量值小的就更新。
剪枝的条件就是如果走到当前节点的耗费值>
=目标变量,就直接不再往下面走,向上走。
深度优先=递归
递归基:
如果到达叶子节点的上一个节点,那么就进行是否更新的判断
递归步:
如果没有到达叶子节点,就进行剪枝操作,判断能否进入下一个节点,如果能,更新最优值
2.3.3关键实现(回溯法+深度优先搜索策略)
1//递归基:
如果已经遍历到叶子节点的上一层节点,i标识递归深度
if(i==g_n)
{
//判断累加和是否超过最大值,如果有0,应该排除;
满足这个条件,才打印
if((g_iArr[pArr[i-1]][pArr[i]]!
=0)&
&
(g_iArr[pArr[g_n]][1]!
(g_iCurResult+g_iArr[pArr[i-1]][pArr[i]]+g_iArr[pArr[g_n]][1]<
g_iResult))
{
g_iResult=g_iCurResult+g_iArr[pArr[i-1]][pArr[i]]+g_iArr[pArr[g_n]][1];
//用当前最优路径去更新最优路径,防止下一次没有
for(intk=1;
k<
=g_n;
k++)
{
g_iBestPath[k]=pArr[k];
2//递归步:
判断能否进入子树,需要尝试每一个节点
else
//尝试不同的组合
for(intj=i;
j<
j++)
//判断能否进入子树:
如果当前值+下一个连线值的和<
最优值,就进入,0要pass
if((g_iArr[pArr[i-1]][pArr[j]]!
(g_iCurResult+g_iArr[pArr[i-1]][pArr[j]]<
g_iResult))
3//交换i与j,则i为当前可以尝试的范围
//为完成后面k个元素的排列,逐一对数组第n-k~n个元素互换。
数组第一个元素为1,生成后面n-1个元素的排列
//数组第一个元素与第二个元素互换,第一个元素为2,第2个元素为1,生成后面的n-1个元素的排列...
swap(&
pArr[i],&
pArr[j]);
//更新当前累加值,是i-1与i的
g_iCurResult+=g_iArr[pArr[i-1]][pArr[i]];
//递归
backTrace(i+1,pArr);
//回溯,清空累加值;
能够走到这里,说明上述结果不是最优解,需要向求解树上一层回退
g_iCurResult-=g_iArr[pArr[i-1]][pArr[i]];
*/
2.3.4时间复杂性
T=O(n!
),该方法并没有有效的提高时间效率。
3结论
本文主要重点讨论了动态规划法、贪心法、回溯法和深度优先搜索策略求解TSP问题算法,并附录给出了相应程序,并通过对比得到动态规划法和贪心法相对更有优势,下面对这两种方法进行详述和进一步对比。
3.1动态规划法思想
动态规划法中对于顶点元素生成的子集本文中用字符串形式存储,然后再用递归方法按照子集中元素个数从小到大开始赋值。
因为后面元素个数较多的子集与前面比其元素个数少1的子集间有一定对应关系,所以用递归方式,可以简便很多。
个人觉得这算本文的一大特色。
另,在计算d[i][j]=min(c[i][k]+d[k][j-1])时,获得d[k][j-1]的过程比较困难,运用字符串后,我们就可以首先找到指定字符,然后去掉该字符,返回剩余字符串,在与V[]逐个比较,找到与其相等的V[]中元素对应下标,此下标即为j-1;
具体求解过程可参考附录源程序,有详细说明。
在求解最佳路径所经过城市顺序时,本文是通过边查找d[i][j]边记录路径的,这样可以省掉很多麻烦,另,路径也是采用字符串形式的数组,数组规模与存储城市间距离的c[][]数组相同,由于很多元素均不需赋值,这样做可能会浪费内存空间,但是目前还没找到更好地求解方法。
3.2贪心法思想
贪心法中,由于贪心法相对动态规划法要简单很多,每次在查找最近城市时所得的顶点均为最后该法最佳路径所经过的城市编号,规模相对较小,容易确定,操作相对简单,所以本文用数组V[]存放最佳路径所经过的城市编号顺序相对来说方便很多。
另外,本文用path[]整型数组存放所经路径的长度,最后相加即可得最短路径。
3.3两者比较
动态规划法相对贪心法来说虽然要精确些,但代码相对繁杂很多,对时间和空间要求很多,仅适用于城市数量较小的情况。
贪心法虽然比较简单,实现起来比较容易,但不是很精确,当图中顶点个数较多并且各边的代价值分布比较均匀时,贪心法可以给出较好的近似解,不过,这个近似解以何种程度近似于最优解,却难以保证。
另外,动态规划法有一个明显的缺点,就是出发城市只能是第0个城市(城市从0开始编号),若出发城市改变,则必须以该城市为第0个城市顺序给其他城市编号,输入城市间距离。
由于若出发城市任意,编码的难度大大增加,所以最后不得已放弃,但这大大地限制了程序的通用性。
而对于贪心法,本文很好地避免了这个问题,一旦城市编号确定,可以从任意城市出发,这也是本文中贪心法优于动态规划法的一点。
3.4优点
本文程序优点,各个子函数功能分隔很明显,没有大量集中在一个函数里面,而是分成了几个不同功能的小函数,这样程序可阅读性提高。
另外,程序中有详细注释,程序中变量取名都是根据变量的性质和所代表的含义命名的,也相应提高了程序的可读性。
对于动态规划法,城市个数可以在算法时间允许的范围内任意,于这点来说,通用性较好;
对于贪心法,出发城市可以任意,城市个数也可以任意,通用性较好。
3.5建议
当城市个数较少时,用动态规划法求出最优解;
当城市个数较多并且各边的代价值分布比较均匀时,贪心法可以给出较好的近似解。
4参考文献
(1)《计算机算法分析与设计》第二版,王晓东编著,电子工业出版社
(2)Java语言与面向对象程序设计(第2版)印旻、王行言编著,清华大学出版社
(3)求解TSP算法,周康、强小利、同小军、许进,计算机工程与应用
(4)《算法设计与分析》,王红梅编著,清华大学出版社
(5)《ACM/ICPC算法训练教程》,余立功主编,清华大学出版社
6附录
6.1动态规划法
6.1.1源代码
packageTsp;
importjava.util.Scanner;
publicclassTSPDP{
String[]V;
//顶点生成的子集,这里把每一个子集用一个字符串表示
int[][]c;
//顶点间距离
int[][]d;
//存放迭代结果
intN;
//城市个数
String[][]path;
//用于存放每种选择下经过的城市
staticintIFINITE=99999;
//无穷大距离表示城市自己到达自己时,距离无穷大,不作为考虑因素
//构造函数
publicTSPDP(){
initialC();
initialV1();
}
//初始化数组c[],即顶点间距离
publicvoidinitialC(){
Scannerin=newScanner(System.in);
System.out.println("
请输入城市个数:
(注意根据实际情况城市个数不可小于1!
)"
);
N=in.nextInt();
if(N<
=1){
System.out.println("
不符合要求,请认真核对!
"
System.exit(0);
//输入错误,结束!
}
请输入城市相邻城市间距离(城市从0开始编号,且出发城市为第0个城市!
):
"
c=newint[N][N];
//为c分配空间
for(inti=0;
i<
N;
i++)
for(intj=0;
j++){
c[i][j]=in.nextInt();
//输入时,按城市编号从小到大,如若两城市间没有公路相连,则距离为无穷大。
本城市与本城市间距离也为无穷大。
}
//初始化顶点生成的子集的对外调用函数
publicvoidinitialV1(){
V=newString[(int)Math.pow(2,N-1)];
//为V分配空间
initialV(0,0);
//具体的初始化顶点生成的子集
//本程序使用递归调用方法初始化V,并按照数字大小顺序排序。
。
另,子集使用字符型形式存放的
//我们是按照子集中元素个数从小到大逐个添加的,后面的子集是前面对应子集加上一个元素组成的,故用递归
publicvoidinitialV(intm,intlen){//m代表下一个即将初始化的V数组的元素的下标;
len是最后一个初始化的元素的长度
if(m>
(int)Math.pow(2,N-1)-1)
return;
//如果全部顶点已初始化完成,则返回。
if(m==0)
V[m++]="
;
//初始化出发顶点,即V[0]
else{
inti=m-1;
while(i>
=0&
V[i].length()==len)
//找与最后一个初始化的V[m-1]子集内元素个数相同的集合,把指针i指向满足条件的集合
i--;
i++;
//把指针i指向满足条件的第一个集合
while(i<
m){
intch;
//用于表示下一个即将加入子集的数字
if(i==0)
ch=0;
//如果i指向V中第一个元素
else{
StringchStr="
+V[i].charAt(V[i].length()-1);
//找出V[i]中最后一个数字
ch=Integer.parseInt(chStr);
//转换成整型
}
//比ch大而又比N-1(因为这里顶点是从0开始的)小的数字应该加在子集中
while(ch<
N-1)
V[m++]=V[i]+(++ch);
i++;
//对已存在的自己逐个扫描添加
initialV(m,V[m-1].length());
//递归调用
//判断自己V[j]中是否存在指定元素,即行号i
booleanexclude(inti,intj){
Stringstr="
+i;
//把i转换成字符串
if(V[j].contains(str))
//{System.out.println(i+"
i"
returnfalse;
//如若存在,则返回false
else
returntrue;
//获得子集V[j]中除指定元素k外的元素,用字符串形式表示
publicStringgetSubString(intk,intj){
if(V[j].length()==1)
return"
//如果子集中只有一个元素,则返回空串
if(k==0)
returnV[j].substring(1,V[j].length());
//如果k是第一个元素,则返回其后面的元素
elseif(k==V[j].length()-1)
returnV[j].substring(0,V[j].length()-1);
//如果k是最后一个元素,则返回其前面的元素
else
return(V[j].substring(0,k)+V[j].substring(k+1,
V[j].length()));
//返回除k外的元素
//找出V[]中与str相同元素的下标号,即找出上一个子集
publicintstringEqual(Stringstr){
//if(str.equals("
))return0;
inti=0;
while(i<
V.length){
if(V[i].equals(str))
returni;
return-1;
//如若没找到,则返回错误符号-1
//求最小距离
publicintmin(inti,intj){
intk=0;
//用于记录V[j]中元素个数
StringvStr="
+V[j].charAt(k);
//铭记V[j].charAt(k)得到的是字符型,转换成整形后是字母对应的ASC码!
!
intv=Integer.parseInt(vStr);
//把位置k处的字符转换成整形
Stringstr=getSubString(k,j);
//获得V[j]中除位置k处外的字符串
//System.out.println("
min"
+str+stringEqual(str)+v);
if(stringEqual(str)==-1)
intmin=c[i][v]+d[v][stringEqual(str)];
//先把最小的距离赋值给从V[j]中第一个顶点出发的距离
//System.out.println(min);
////stringEqual(str)表示返回与上面获得的字符串相同的V中元素的下标,即找上一个子集
path[i][j]=path[v][stringEqual(str)]+i;
k++;
//寻找最小距离
while(k<
V[j].length()){
vStr="
v=Integer.parseInt(vStr);
str=getSubString(k,j);
if(min>
c[i][v]+d[v][stringEqual(str)]){
min=c[i][v]+d[v][stringEqual(str)];
path[i][j]=path[v][stringEqual(str)]+i;
k++;
//V[j].substring(beginIndex,endIndex)
//System.out.println(path[i][j]);
returnmin;
//返回最小值
//处理函数
publicvoiddynamic(){
d=newint[N][(int)Math.pow(2,N-1)];
//分配空间
path=newString[N][(int)Math.pow(2,N-1)];
for(inti=1;
i++){//初始化第一列
d[i][0]=c[i][0];
path[i][0]="
0"
//初始化第一个元素,即为出发城市顶点
//System.out.print(d[i][0]+"
//初始化后面的元素
intj=1;
for(;
(int)Math.pow(2,N-1)-1;
for(inti=1;
i++){
if(exclude(i,j))//判断V子集中是否包含当前顶点,即V[j]中是否包含i
{
//System.out.println("
done!
+i+"
+j);
d[i][j]=min(i,j);
//寻找最小距离
d[0][j]=min(0,j);
//初始化组后一列
//输出中间结果,各个数组,用于调试程序
publicvoidprint(){
显示定点生成子集:
(int)Math.pow(2,N-1);
System.out.print(V[i]+"
//for(inti=0;
c.length;
)
System.out.println();
打印代价矩阵:
System.out.print(c[i][j]+"
System.out.println();
System.
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 算法 报告 旅行 问题 模板 汇编