旅行商问题的求解方法动态规划法和贪心法算法word版.docx

上传人:b****8 文档编号:28184503 上传时间:2023-07-09 格式:DOCX 页数:25 大小:149.36KB
下载 相关 举报
旅行商问题的求解方法动态规划法和贪心法算法word版.docx_第1页
第1页 / 共25页
旅行商问题的求解方法动态规划法和贪心法算法word版.docx_第2页
第2页 / 共25页
旅行商问题的求解方法动态规划法和贪心法算法word版.docx_第3页
第3页 / 共25页
旅行商问题的求解方法动态规划法和贪心法算法word版.docx_第4页
第4页 / 共25页
旅行商问题的求解方法动态规划法和贪心法算法word版.docx_第5页
第5页 / 共25页
点击查看更多>>
下载资源
资源描述

旅行商问题的求解方法动态规划法和贪心法算法word版.docx

《旅行商问题的求解方法动态规划法和贪心法算法word版.docx》由会员分享,可在线阅读,更多相关《旅行商问题的求解方法动态规划法和贪心法算法word版.docx(25页珍藏版)》请在冰豆网上搜索。

旅行商问题的求解方法动态规划法和贪心法算法word版.docx

旅行商问题的求解方法动态规划法和贪心法算法word版

旅行商问题的求解方法

摘要

旅行商问题(TSP问题)时是指旅行家要旅行n个城市然后回到出发城市,要求各个城市经历且仅经历一次,并要求所走的路程最短。

该问题又称为货郎担问题、邮递员问题、售货员问题,是图问题中最广为人知的问题。

本文主要介绍用蛮力法、动态规划法、贪心法和分支限界法求解TSP问题,其中重点讨论动态规划法和贪心法,并给出相应求解程序。

关键字:

旅行商问题;动态规划法;贪心法;分支限界法

1引言

旅行商问题(TSP)是组合优化问题中典型的NP-完全问题,是许多领域内复杂工程优化问题的抽象形式。

研究TSP的求解方法对解决复杂工程优化问题具有重要的参考价值。

关于TSP的完全有效的算法目前尚未找到,这促使人们长期以来不断地探索并积累了大量的算法。

归纳起来,目前主要算法可分成传统优化算法和现代优化算法。

在传统优化算法中又可分为:

最优解算法和近似方法。

最优解算法虽然可以得到精确解,但计算时间无法忍受,因此就产生了各种近似方法,这些近似算法虽然可以较快地求得接近最优解的可行解,但其接近最优解的程度不能令人满意。

但限于所学知识和时间限制,本文重点只讨论传统优化算法中的动态规划法、贪心法和分支限界法,并对蛮力法做简单介绍,用以比较。

2正文

2.1蛮力法

2.1.1蛮力法的设计思想

蛮力法所依赖的基本技术是扫描技术,即采用一定的策略将待求解问题的所有元素一次处理一次,从而找出问题的解。

一次处理所有元素的是蛮力法的关键,为了避免陷入重复试探,应保证处理过的元素不再被处理。

在基本的数据结构中,一次处理每个元素的方法是遍历。

2.1.2算法讨论

用蛮力法解决TSP问题,可以找出所有可能的旅行路线,从中选取路径长度最短的简单回路。

如对于图1,我们求解过程如下:

(1)路径:

1->2->3->4->1;路径长度:

18;

(2)路径:

1->2->4->3->1;路径长度:

11;

(3)路径:

1->3->2->4->1;路径长度:

23;

(4)路径:

1->3->4->2->1;路径长度:

11;

(5)路径:

1->4->2->3->1;路径长度:

18;

(6)路径:

1->4->3->2->1;路径长度:

18;

从中,我们可以知道,路径

(2)和(4)路径长度最短。

我们还应注意到,图1中,有3对不同的路径,对每对路径来说,不同只是路径的方向,因此,可以将这个数量减半,则可能的解有(n-1)!

/2个。

这是一个非常大的数,随着n的增长,TSP问题的可能解也在迅速增长。

如:

一个10城市的TSP问题有大约有180,000个可能解。

一个20城市的TSP问题有大约有60,000,000,000,000,000个可能解。

一个50城市的TSP问题有大约1062个可能解,而一个行星上也只有1021升水。

因此,我们可以知道用蛮力法求解TSP问题,只能解决问题规模很小的实例。

2.2动态规划法

2.2.1动态规划法的设计思想

动态规划法将待求解问题分解成若干个相互重叠的子问题,每个子问题对应决策过程的一个阶段,一般来说,子问题的重叠关系表现在对给定问题求解的递推关系(也就是动态规划函数)中,将子问题的解求解一次并填入表中,当需要再次求解此子问题时,可以通过查表获得该子问题的解而不用再次求解,从而避免了大量重复计算。

2.2.2TSP问题的动态规划函数

假设从顶点i出发,令

表示从顶点i出发经过

中各个顶点一次且仅一次,最后回到出发点i的最短路径长度,开始时,

,于是,TSP问题的动态规划函数为:

2.2.3算法讨论

(1)for(i=1;i

d[i][0]=c[i][0];

(2)for(j=1;j<

-1;j++)

for(i=1;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.3.4时间复杂性

和蛮力法相比,动态规划法求解TSP问题,把原来的时间复杂性是O(n!

)的排列问题,转化为组合问题,从而降低了算法的时间复杂性,但它仍需要指数时间。

2.3贪心法

2.3.1贪心法的设计思想

贪心法在解决问题的策略上目光短浅,只根据当前已有的信息就做出选择,而且一旦做出了选择,不管将来有什么结果,这个选择都不会改变。

换言之,贪心法并不是从整体最优考虑,它所做出的选择只是在某种意义上的局部最优。

这种局部最优选择并不总能获得整体最优解,但通常能获得近似最优解。

2.3.2最近邻点策略求解TSP问题

贪心法求解TSP问题的贪心策略是显然的,至少有两种贪心策略是合理的:

最近邻点策略和最短链接策略。

本文仅重点讨论最近邻点策略及其求解过程。

最近邻点策略:

从任意城市出发,每次在没有到过的城市中选择距离已选择的城市中最近的一个,直到经过了所有的城市,最后回到出发城市。

2.3.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.3.4时间复杂性

但需注意,用最近邻点贪心策略求解TSP问题所得的结果不一定是最优解。

当图中顶点个数较多并且各边的代价值分布比较均匀时,最近邻点策略可以给出较好的近似解,不过,这个近似解以何种程度近似于最优解,却难以保证。

2.4分支限界法

2.4.1分支限界法的设计思想

假设求解最大化问题,解向量为

,其中,

的取值范围为某个有穷集合

在使用分支限界法搜索问题的解空间树时,首先根据限界函数估算目标函数的界[down,up],然后从根结点出发,扩展根结点的

个孩子结点,从而构成分量

种可能的取值方式。

对这

个孩子结点分别估算可能取得的目标函数值

,其含义是以该孩子结点为根的子树所可能取得的目标函数值不大于

,也就是部分解应满足:

本文本欲详细讨论该算法,但无奈在编程问题中,尚有问题有待解决,时间所限,不得已放弃。

本人编程过程中所用算法思想与老师课上所教略有不同,在寻找下界时,是首先把每个结点所能到达的各个结点及其可能的路径算出来,并添加到PT表中,但最后,不知是何原因,在还有一个城市尚未加入时,PT表的添加出现了问题,思忖良久,仍未解决,时间所限,迫不得已,留待以后有时间再另行研究,本文就只给出动态规划法和贪心法的具体求解过程。

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优点

本文程序优点,各个子函数功能分隔很明显,没有大量集中在一个函数里面,而是分成了几个不同功能的小函数,这样程序可阅读性提高。

另外,程序中有详细注释,程序中变量取名都是根据变量的性质和所代表的含义命名的,也相应提高了程序的可读性。

对于动态规划法,城市个数可以在算法时间允许的范围内任意,于这点来说,通用性较好;对于贪心法,出发城市可以任意,城市个数也可以任意,通用性较好。

4建议

当城市个数较少时,用动态规划法求出最优解;当城市个数较多并且各边的代价值分布比较均匀时,贪心法可以给出较好的近似解。

5参考文献

(1)《计算机算法分析与设计》第二版,王晓东编著,电子工业出版社

(2)Java语言与面向对象程序设计(第2版)印旻、王行言编著,清华大学出版社

(3)求解TSP算法,周康、强小利、同小军、许进,计算机工程与应用

6附录

6.1动态规划法

6.1.1源代码

packageexp2;

importjava.util.Scanner;

publicclassTSPDynamic{

String[]V;//顶点生成的子集,这里把每一个子集用一个字符串表示

int[][]c;//顶点间距离

int[][]d;//存放迭代结果

intN;//城市个数

String[][]path;//用于存放每种选择下经过的城市

staticintIFINITE=99999;//无穷大距离表示城市自己到达自己时,距离无穷大,不作为考虑因素

//构造函数

publicTSPDynamic(){

initialC();

initialV1();

}

//初始化数组c[],即顶点间距离

publicvoidinitialC(){

Scannerin=newScanner(System.in);

System.out.println("请输入城市个数:

(注意根据实际情况城市个数不可小于1!

)");

N=in.nextInt();

if(N<=1){

System.out.println("不符合要求,请认真核对!

");

System.exit(0);//输入错误,结束!

}

System.out.println("请输入城市相邻城市间距离(城市从0开始编号,且出发城市为第0个城市!

):

");

c=newint[N][N];//为c分配空间

for(inti=0;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

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

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

elsereturntrue;

}

//获得子集V[j]中除指定元素k外的元素,用字符串形式表示

publicStringgetSubString(intk,intj){

if(V[j].length()==1)return"";//如果子集中只有一个元素,则返回空串

else{

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是最后一个元素,则返回其前面的元素

elsereturn(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

if(V[i].equals(str))

returni;

i++;

}

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)System.exit(0);

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

vStr=""+V[j].charAt(k);

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"+i;//初始化第一个元素,即为出发城市顶点

//System.out.print(d[i][0]+"");

}

//初始化后面的元素

intj=1;

for(;j<(int)Math.pow(2,N-1)-1;j++)

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(){

for(inti=0;i<(int)Math.pow(2,N-1);i++)

System.out.print(V[i]+"");

//for(inti=0;i

System.out.println();

for(inti=0;i

for(intj=0;j

System.out.print(c[i][j]+"");

System.out.println();

}

for(inti=0;i

for(intj=0;j<(int)Math.pow(2,N-1);j++)

System.out.print(d[i][j]+"");

System.out.println();

}

}

//输出最短路径

publicvoidprintShortestPath(){

//输出所经城市

System.out.print("经过城市:

");

Stringstr=path[0][(int)Math.pow(2,N-1)-1];

//System.out.println(str);

System.out.print(str.charAt(str.length()-1));

for(inti=str.length()-2;i>=0;i--){

System.out.print("->"+str.charAt(i));

}

System.out.println("会有最短路径");

System.out.println("最短路径为:

"+d[0][(int)Math.pow(2,N-1)-1]);

}

//主函数

publicstaticvoidmain(String[]args){

TSPDynamicTSP=newTSPDynamic();

TSP.dynamic();//求最短路径

//TSP.print();

TSP.printShortestPath();//输出最短路径

}

}

//测试数据

/*99999367

59999923

64999992

37599999

*/

6.1.2结果

(1)

(2)

(3)

(4)

6.2贪心法

6.2.1源代码

packageexp2;

importjava.util.Scanner;

publicclassTSPGreedNode{

int[]V;//存放旅行所经过的城市顶点

int[][]c;//存放每两座城市间的距离,注意:

若路径不存在或同一城市间距离为无穷大

in

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 自然科学 > 物理

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1