基于粒子群算法求解求解TSP问题(JAVA)
一、TSP问题
TSP问题(Travelling Salesman Problem)即旅行商问题,又译为旅行推销员问题、货郎担问题,是数学领域中著名问题之一。假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。
TSP问题是一个组合优化问题。该问题可以被证明具有NPC计算复杂性。TSP问题可以分为两类,一类是对称TSP问题(Symmetric TSP),另一类是非对称问题(Asymmetric TSP)。所有的TSP问题都可以用一个图(Graph)来描述:
V={c1, c2, …, ci, …, cn},i = 1,2, …, n,是所有城市的集合.ci表示第i个城市,n为城市的数目;
E={(r, s): r,s∈ V}是所有城市之间连接的集合;
C = {crs: r,s∈ V}是所有城市之间连接的成本度量(一般为城市之间的距离);
如果crs = csr, 那么该TSP问题为对称的,否则为非对称的。
一个TSP问题可以表达为:
求解遍历图G = (V, E, C),所有的节点一次并且回到起始节点,使得连接这些节点的路径成本最低。
二、粒子群算法
1、基本思想
粒子群算法简称PSO,它的基本思想是模拟鸟群的捕食行为。设想这样一个场景:一群鸟在随机搜索食物。在这个区域里只有一块食物。所有的鸟都不知道食物在那里。但是他们知道当前的位置离食物还有多远。那么找到食物的最优策略是什么呢。最简单有效的就是搜寻目前离食物最近的鸟的周围区域。
PSO从这种模型中得到启示并用于解决优化问题。PSO中,每个优化问题的解都是搜索空间中的一只鸟。我们称之为“粒子”。所有的粒子都有一个由被优化的函数决定的适应值(fitness value),每个粒子还有一个速度决定他们飞翔的方向和距离。然后粒子们就追随当前的最优粒子在解空间中搜索。
PSO 初始化为一群随机粒子(随机解)。然后通过迭代找到最优解。在每一次迭代中,粒子通过跟踪两个"极值"来更新自己。第一个就是粒子本身所找到的最优解,这个解叫做个体极值pBest。另一个极值是整个种群目前找到的最优解,这个极值是全局极值gBest。另外也可以不用整个种群而只是用其中一部分作为粒子的邻居,那么在所有邻居中的极值就是局部极值。
2、粒子公式
在找到这两个最优值时,粒子根据如下的公式来更新自己的速度和新的位置:
v[i] = w * v[i] + c1 * rand() * (pbest[i] - present[i]) + c2 * rand() * (gbest - present[i])
present[i] = present[i] + v[i]
其中v[i]代表第i个粒子的速度,w代表惯性权值,c1和c2表示学习参数,rand()表示在0-1之间的随机数,pbest[i]代表第i个粒子搜索到的最优值,gbest代表整个集群搜索到的最优值,present[i]代表第i个粒子的当前位置。
3、个人见解
截止目前为止,粒子群优化算法大体上可以分为两大类,一类是最初的基本粒子群优化算法,一类是改进后的广义粒子群优化算法,其实PSO最初的设计主要用于处理连续优化问题,如求函数极值,在复杂的组合优化问题上它的应用相当有限,后来经过众多学者的改进才将其应用于求解TSP和单机调度之类的问题。广义粒子群算法模型和遗传算法相当类似,目前网上有关于粒子群算法求解TSP的很多论文或代码都是基于广义粒子群算法的,说简单点就是进化思想,用交叉变异代替了基本粒子群算法的迭代公式,当然他们也还是有粒子群优化的本质思想的,如与全局最优编码交叉,与局部最优编码交叉,变异等都是源自于基本粒子群算法的迭代公式。
三、粒子群优化算法求解TSP问题
关于基本粒子群优化算法的使用,可参考最近这篇文章,
自话粒子群算法 ,既然是求解TSP,使用基本的粒子群算法迭代公式肯定是不行的,在这里我也不想写一个与遗传算法差不多的粒子群算法,确实没必要,只要看了解遗传算法,分分钟就能写出来,在这里我想使用一种,比较特别的迭代方式,先来看看它的迭代公式:
在这里我不多啰嗦了,想要详细了解,可能你需要看一下这篇论文:http://download.csdn.net/detail/wangqiuyun/6373499 有点古老是吧,没关系,要的是它的思想。
我们使用TSP问题依然来自于tsplib上的att48,这是一个对称TSP问题,城市规模为48,其最优值为10628.其距离计算方法下图所示:
好,下面是具体代码:
package noah; import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Random; public class PSO { private int bestNum;
private float w;
private int MAX_GEN;// 迭代次数
private int scale;// 种群规模 private int cityNum; // 城市数量,编码长度
private int t;// 当前代数 private int[][] distance; // 距离矩阵 private int[][] oPopulation;// 粒子群
private ArrayList<ArrayList<SO>> listV;// 每科粒子的初始交换序列 private int[][] Pd;// 一颗粒子历代中出现最好的解,
private int[] vPd;// 解的评价值 private int[] Pgd;// 整个粒子群经历过的的最好的解,每个粒子都能记住自己搜索到的最好解
private int vPgd;// 最好的解的评价值
private int bestT;// 最佳出现代数 private int[] fitness;// 种群适应度,表示种群中各个个体的适应度 private Random random; public PSO() { } /**
* constructor of GA
*
* @param n
* 城市数量
* @param g
* 运行代数
* @param w
* 权重
**/
public PSO(int n, int g, int s, float w) {
this.cityNum = n;
this.MAX_GEN = g;
this.scale = s;
this.w = w;
} // 给编译器一条指令,告诉它对被批注的代码元素内部的某些警告保持静默
@SuppressWarnings("resource")
/**
* 初始化PSO算法类
* @param filename 数据文件名,该文件存储所有城市节点坐标数据
* @throws IOException
*/
private void init(String filename) throws IOException {
// 读取数据
int[] x;
int[] y;
String strbuff;
BufferedReader data = new BufferedReader(new InputStreamReader(
new FileInputStream(filename)));
distance = new int[cityNum][cityNum];
x = new int[cityNum];
y = new int[cityNum];
for (int i = 0; i < cityNum; i++) {
// 读取一行数据,数据格式1 6734 1453
strbuff = data.readLine();
// 字符分割
String[] strcol = strbuff.split(" ");
x[i] = Integer.valueOf(strcol[1]);// x坐标
y[i] = Integer.valueOf(strcol[2]);// y坐标
}
// 计算距离矩阵
// ,针对具体问题,距离计算方法也不一样,此处用的是att48作为案例,它有48个城市,距离计算方法为伪欧氏距离,最优值为10628
for (int i = 0; i < cityNum - 1; i++) {
distance[i][i] = 0; // 对角线为0
for (int j = i + 1; j < cityNum; j++) {
double rij = Math
.sqrt(((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j])
* (y[i] - y[j])) / 10.0);
// 四舍五入,取整
int tij = (int) Math.round(rij);
if (tij < rij) {
distance[i][j] = tij + 1;
distance[j][i] = distance[i][j];
} else {
distance[i][j] = tij;
distance[j][i] = distance[i][j];
}
}
}
distance[cityNum - 1][cityNum - 1] = 0; oPopulation = new int[scale][cityNum];
fitness = new int[scale]; Pd = new int[scale][cityNum];
vPd = new int[scale]; /*
* for(int i=0;i<scale;i++) { vPd[i]=Integer.MAX_VALUE; }
*/ Pgd = new int[cityNum];
vPgd = Integer.MAX_VALUE; // nPopulation = new int[scale][cityNum]; bestT = 0;
t = 0; random = new Random(System.currentTimeMillis());
/*
* for(int i=0;i<cityNum;i++) { for(int j=0;j<cityNum;j++) {
* System.out.print(distance[i][j]+","); } System.out.println(); }
*/ } // 初始化种群,多种随机生成办法
void initGroup() {
int i, j, k;
for (k = 0; k < scale; k++)// 种群数
{
oPopulation[k][0] = random.nextInt(65535) % cityNum;
for (i = 1; i < cityNum;)// 粒子个数
{
oPopulation[k][i] = random.nextInt(65535) % cityNum;
for (j = 0; j < i; j++) {
if (oPopulation[k][i] == oPopulation[k][j]) {
break;
}
}
if (j == i) {
i++;
}
}
} /*
* for(i=0;i<scale;i++) { for(j=0;j<cityNum;j++) {
* System.out.print(oldPopulation[i][j]+","); } System.out.println(); }
*/
} void initListV() {
int ra;
int raA;
int raB; listV = new ArrayList<ArrayList<SO>>(); for (int i = 0; i < scale; i++) {
ArrayList<SO> list = new ArrayList<SO>();
ra = random.nextInt(65535) % cityNum;
for (int j = 0; j < ra; j++) {
raA = random.nextInt(65535) % cityNum;
raB = random.nextInt(65535) % cityNum;
while (raA == raB) {
raB = random.nextInt(65535) % cityNum;
} // raA与raB不一样
SO s = new SO(raA, raB);
list.add(s);
} listV.add(list);
}
} public int evaluate(int[] chr) {
// 0123
int len = 0;
// 编码,起始城市,城市1,城市2...城市n
for (int i = 1; i < cityNum; i++) {
len += distance[chr[i - 1]][chr[i]];
}
// 城市n,起始城市
len += distance[chr[cityNum - 1]][chr[0]];
return len;
} // 求一个基本交换序列作用于编码arr后的编码
public void add(int[] arr, ArrayList<SO> list) {
int temp = -1;
SO s;
for (int i = 0; i < list.size(); i++) {
s = list.get(i);
temp = arr[s.getX()];
arr[s.getX()] = arr[s.getY()];
arr[s.getY()] = temp;
}
} // 求两个编码的基本交换序列,如A-B=SS
public ArrayList<SO> minus(int[] a, int[] b) {
int[] temp = b.clone();
/*
* int[] temp=new int[L]; for(int i=0;i<L;i++) { temp[i]=b[i]; }
*/
int index;
// 交换子
SO s;
// 交换序列
ArrayList<SO> list = new ArrayList<SO>();
for (int i = 0; i < cityNum; i++) {
if (a[i] != temp[i]) {
// 在temp中找出与a[i]相同数值的下标index
index = findNum(temp, a[i]);
// 在temp中交换下标i与下标index的值
changeIndex(temp, i, index);
// 记住交换子
s = new SO(i, index);
// 保存交换子
list.add(s);
}
}
return list;
} // 在arr数组中查找num,返回num的下标
public int findNum(int[] arr, int num) {
int index = -1;
for (int i = 0; i < cityNum; i++) {
if (arr[i] == num) {
index = i;
break;
}
}
return index;
} // 将数组arr下标index1与下标index2的值交换
public void changeIndex(int[] arr, int index1, int index2) {
int temp = arr[index1];
arr[index1] = arr[index2];
arr[index2] = temp;
} // 二维数组拷贝
public void copyarray(int[][] from, int[][] to) {
for (int i = 0; i < scale; i++) {
for (int j = 0; j < cityNum; j++) {
to[i][j] = from[i][j];
}
}
} // 一维数组拷贝
public void copyarrayNum(int[] from, int[] to) {
for (int i = 0; i < cityNum; i++) {
to[i] = from[i];
}
} public void evolution() {
int i, j, k;
int len = 0;
float ra = 0f; ArrayList<SO> Vi; // 迭代一次
for (t = 0; t < MAX_GEN; t++) {
// 对于每颗粒子
for (i = 0; i < scale; i++) {
if(i==bestNum) continue;
ArrayList<SO> Vii = new ArrayList<SO>();
//System.out.println("------------------------------");
// 更新速度
// Vii=wVi+ra(Pid-Xid)+rb(Pgd-Xid)
Vi = listV.get(i); // wVi+表示获取Vi中size*w取整个交换序列
len = (int) (Vi.size() * w);
//越界判断
//if(len>cityNum) len=cityNum;
//System.out.println("w:"+w+" len:"+len+" Vi.size():"+Vi.size());
for (j = 0; j < len; j++) {
Vii.add(Vi.get(j));
} // Pid-Xid
ArrayList<SO> a = minus(Pd[i], oPopulation[i]);
ra = random.nextFloat(); // ra(Pid-Xid)+
len = (int) (a.size() * ra);
//越界判断
//if(len>cityNum) len=cityNum;
//System.out.println("ra:"+ra+" len:"+len+" a.size():"+a.size());
for (j = 0; j < len; j++) {
Vii.add(a.get(j));
} // Pid-Xid
ArrayList<SO> b = minus(Pgd, oPopulation[i]);
ra = random.nextFloat(); // ra(Pid-Xid)+
len = (int) (b.size() * ra);
//越界判断
//if(len>cityNum) len=cityNum;
//System.out.println("ra:"+ra+" len:"+len+" b.size():"+b.size());
for (j = 0; j < len; j++) {
SO tt= b.get(j);
Vii.add(tt);
} //System.out.println("------------------------------Vii.size():"+Vii.size()); // 保存新Vii
listV.add(i, Vii); // 更新位置
// Xid’=Xid+Vid
add(oPopulation[i], Vii);
} // 计算新粒子群适应度,Fitness[max],选出最好的解
for (k = 0; k < scale; k++) {
fitness[k] = evaluate(oPopulation[k]);
if (vPd[k] > fitness[k]) {
vPd[k] = fitness[k];
copyarrayNum(oPopulation[k], Pd[k]);
bestNum=k;
}
if (vPgd > vPd[k]) {
System.out.println("最佳长度"+vPgd+" 代数:"+bestT);
bestT = t;
vPgd = vPd[k];
copyarrayNum(Pd[k], Pgd);
}
}
}
} public void solve() {
int i;
int k; initGroup();
initListV(); // 每颗粒子记住自己最好的解
copyarray(oPopulation, Pd); // 计算初始化种群适应度,Fitness[max],选出最好的解
for (k = 0; k < scale; k++) {
fitness[k] = evaluate(oPopulation[k]);
vPd[k] = fitness[k];
if (vPgd > vPd[k]) {
vPgd = vPd[k];
copyarrayNum(Pd[k], Pgd);
bestNum=k;
}
} // 打印
System.out.println("初始粒子群...");
for (k = 0; k < scale; k++) {
for (i = 0; i < cityNum; i++) {
System.out.print(oPopulation[k][i] + ",");
}
System.out.println();
System.out.println("----" + fitness[k]); /*
ArrayList<SO> li = listV.get(k);
int l = li.size();
for (i = 0; i < l; i++) {
li.get(i).print();
} System.out.println("----");
*/
} // 进化
evolution(); // 打印
System.out.println("最后粒子群...");
for (k = 0; k < scale; k++) {
for (i = 0; i < cityNum; i++) {
System.out.print(oPopulation[k][i] + ",");
}
System.out.println();
System.out.println("----" + fitness[k]); /*
ArrayList<SO> li = listV.get(k);
int l = li.size();
for (i = 0; i < l; i++) {
li.get(i).print();
} System.out.println("----");
*/
} System.out.println("最佳长度出现代数:");
System.out.println(bestT);
System.out.println("最佳长度");
System.out.println(vPgd);
System.out.println("最佳路径:");
for (i = 0; i < cityNum; i++) {
System.out.print(Pgd[i] + ",");
} } /**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
System.out.println("Start...."); PSO pso = new PSO(48, 5000, 30, 0.5f);
pso.init("c://data.txt");
pso.solve();
}
}
运行结果截图:
四、总结
对于这个实验结果,其实我个人是不能接受的,但是目前我只能把问题归结于那篇论文提出的迭代公式上,不过那论文年代久远,它上面就试了14个点,这里是48个,差距肯定是有的,或许这里再进行一些调整,没准效果会好些,但是目前我本人时间有限,还来不及深入实验,只能让感兴趣的人来进行深入剥解!
基于粒子群算法求解求解TSP问题(JAVA)的更多相关文章
- 基于粒子群算法的分组背包MATLAB实现
抽空看了一段时间的粒子群算法,这里仅针对其应用于动态规划中的背包问题的情况做下总结归纳,其他应用可以之后想到了再添加. 一:分组背包问题简介 假设有3个组,每组有2个物品,每种物品有3种属性,价值.体 ...
- 基于粒子群优化的无约束50维Rosenbrock函数求解
基于粒子群优化的无约束50维Rosenbrock函数求解 一.问题重述 无约束50维的Rosenbrock函数可以描述如下: 其中, 0 要求按PSO算法思想设计一个该问题的求解算法. Rosenbr ...
- 算法(三)粒子群算法PSO的介绍
一.引言 在讲算法之前,先看两个例子: 例子一:背包问题,一个书包,一堆物品,每个物品都有自己的价值和体积,装满书包,使得装的物品价值最大. 例子二:投资问题,n个项目,第i个项目投资为ci 收益为p ...
- 粒子群算法(PSO)算法解析(简略版)
粒子群算法(PSO) 1.粒子群算法(PSO)是一种基于群体的随机优化技术: 初始化为一组随机解,通过迭代搜寻最优解. PSO算法流程如图所示(此图是从PPT做好,复制过来的,有些模糊) 2.PSO模 ...
- 【比较】粒子群算法PSO 和 遗传算法GA 的相同点和不同点
目录 PSO和GA的相同点 PSO和GA不同点 粒子群算法(PSO)和遗传算法(GA)都是优化算法,都力图在自然特性的基础上模拟个体种群的适应性,它们都采用一定的变换规则通过搜索空间求解. PSO和G ...
- C语言实现粒子群算法(PSO)二
上一回说了基本粒子群算法的实现,并且给出了C语言代码.这一篇主要讲解影响粒子群算法的一个重要参数---w.我们已经说过粒子群算法的核心的两个公式为: Vid(k+1)=w*Vid(k)+c1*r1*( ...
- 【智能算法】粒子群算法(Particle Swarm Optimization)超详细解析+入门代码实例讲解
喜欢的话可以扫码关注我们的公众号哦,更多精彩尽在微信公众号[程序猿声] 01 算法起源 粒子群优化算法(PSO)是一种进化计算技术(evolutionary computation),1995 年由E ...
- C语言实现粒子群算法(PSO)一
最近在温习C语言,看的书是<C primer Plus>,忽然想起来以前在参加数学建模的时候,用过的一些智能算法,比如遗传算法.粒子群算法.蚁群算法等等.当时是使用MATLAB来实现的,而 ...
- 粒子群算法 Particle Swarm Optimization, PSO(转贴收藏)
粒子群算法(1)----粒子群算法简介 http://blog.csdn.net/niuyongjie/article/details/1569671 粒子群算法(2)----标准的粒子群算法 htt ...
随机推荐
- 『奇葩问题集锦』Cannot find module 'webpack/lib/node/NodeTemplatePlugin'
第一步:npm config get prefix ,获取输出path“C:\Users\jaxGu\AppData\Roaming\npm”加上"\node_modules"用于 ...
- html5 API
1.Canvas绘图 2.postMessage跨域.多窗口传输 3.requestAnimationFrame动画 4.PageVisibility API页面可见性 5.File 本地文件操作 6 ...
- asp.net mvc4 Controller与Action执行过程的研究(学习笔记)
当IIS收到一个http请求,把请求信息发给对应的HttpModel(实际是实现类UrlRoutingModule),在HttpModel中会注册HttpApplication 类中的PostReso ...
- 转:Redis Geo: Redis新增位置查询功能
原文来自于:http://www.infoq.com/cn/news/2015/07/redis-geo 移动互联网增进了人与人之间的联系,其中基于位置信息的服务(Location Based Ser ...
- POJ_3321_APPLE_TREE
poj上面的一道求子树上苹果的题目,网上看了很多题解,下面我来回忆一下,基本来源于大神的微博,http://blog.csdn.net/zhang20072844,我来做个搬运工.先将树的n条边上节点 ...
- 创建支持CRUD(增删改查)操作的Web API(二)
一:准备工作 你可以直接下载源码查看 Download the completed project. 下载完整的项目 CRUD是指“创建(C).读取(R).更新(U)和删除(D)”,它们是四个 ...
- FFMPEG之TimeBase成员理解
http://blog.csdn.net/supermanwg/article/details/14521869
- 不重复查询mysql
select EquipmentSID,MIN(MatureTime),MIN(ISlock) from table group by name String sql =” Select * from ...
- WCF中的由于目标计算机积极拒绝,无法连接
1.第一种情况 百度上找到了这篇文章 http://blog.sina.com.cn/s/blog_6b44b2ba01016j0z.html 讲的是使用了using用完之后就释放了,得到启发,仔细 ...
- C#面向对象——方法的重载及构造函数、静态对象。
namespace nameclass2 { class Class2 //方法的重载 { public void Function( string s) { Console.WriteLine(s) ...