粒子群算法求解旅行商问题TSP

写在开头:
最近师妹的结课作业问我,关于使用粒子群求解TSP问题的思路。我想了想,自己去年的作业用的是遗传算法,貌似有些关联,索性给看了看代码。重新学习了一遍粒子群算法,在这里记录一下,算是对知识的总结,巩固一下。


正文部分

本文主要是使用粒子群来求解旅行商问题,即TSP问题,这里主要讲解代码和实现思路,原理会简单带过。详细的具体原理,请读者移步参考链接。
本文将从如下几个方面进行描述:

  • 1. 粒子群简单介绍
  • 2. TSP问题简单介绍
  • 3. JAVA代码实现
  • 4. 运行结果展示
  • 5. 源码下载链接
1. 粒子群简单介绍

粒子群算法简称PSO,它的基本思想是模拟鸟群的捕食行为。设想这样一个场景:一群鸟在随机搜索食物。在这个区域里只有一块食物。所有的鸟都不知道食物在那里。但是他们知道当前的位置离食物还有多远。那么找到食物的最优策略是什么呢。最简单有效的就是搜寻目前离食物最近的鸟的周围区域。

PSO从这种模型中得到启示并用于解决优化问题。PSO中,每个优化问题的解都是搜索空间中的一只鸟。我们称之为“粒子”。所有的粒子都有一个由被优化的函数决定的适应值(fitness value),每个粒子还有一个速度决定他们飞翔的方向和距离。然后粒子们就追随当前的最优粒子在解空间中搜索。

PSO 初始化为一群随机粒子(随机解)。然后通过迭代找到最优解。在每一次迭代中,粒子通过跟踪两个”极值”来更新自己。第一个就是粒子本身所找到的最优解,这个解叫做个体极值pBest。另一个极值是整个种群目前找到的最优解,这个极值是全局极值gBest。另外也可以不用整个种群而只是用其中一部分作为粒子的邻居,那么在所有邻居中的极值就是局部极值。

2. TSP问题简单介绍

TSP问题(Travelling Salesman Problem)即旅行商问题,又译为旅行推销员问题、货郎担问题,是数学领域中著名问题之一。假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。

TSP问题是一个组合优化问题。该问题可以被证明具有NPC计算复杂性。TSP问题可以分为两类,一类是对称TSP问题(Symmetric TSP),另一类是非对称问题(Asymmetric TSP)。

3. JAVA代码实现

首先整理一下具体实现逻辑,具体流程如下:

  • 初始化种群
  • 初始化惯性因子,即自身的交换序列
  • 寻找每个粒子的历代中的最优解
  • 寻找全局最优解
  • 计算每个个体的自身最优解的交换序列
  • 计算每个个体对全局最优解的交换序列
  • 进化——其实就是交换
    交换序列对由3部分组成:自身交换序列、自身最优解的交换序列、全局最优解的交换序列。

  • 打印结果

使用面向对象的思想,首先定义一个city.java城市实体类,有id、name、经度和纬度属性。

public class City {private int mId;       //城市编号private String mName;  //城市名称private float mLongitude; //经度private float mLatitude; //经度public City(int id, String name, float longitude, float latitude) {mId = id;mName = name;mLongitude = longitude;mLatitude = latitude;}

定义一个粒子实体类Unit.java,包括行走城市的路径path和适应度fitness。并添加两个方法,输出城市序列和计算自己的适应度。

private int[] mPath;  //行走的城市路径,存储城市编号private int mFitness; //适应度值,为当前个体走这个路径的总距离。越小越好。public Unit(int[] path) {mPath = path;mFitness = calculateFitness();}public void printPath() {if (mPath == null) {System.out.println("mPath为null,当前个体的路径为空");} else {for (int i = 0; i < mPath.length - 1; i++) {System.out.print(CityLab.getInstance().getmCities().get(mPath[i]).getName() + "——》");}System.out.println(CityLab.getInstance().getmCities().get(mPath[mPath.length - 1]).getName());}}public void upDateFitness() {this.mFitness = calculateFitness();}/*** 计算当前路径的适应值,即为路径长度*/public int calculateFitness() {//根据经纬度计算距离//近似计算:0.00001度,距离相差约1米;0.01,距离相差1000米.1度,距离相差100kmint distance = 0;  //单位千米(km)int n = mPath.length;for (int i = 1; i < n; i++) {City c1 = CityLab.getInstance().getmCities().get(mPath[i - 1]);City c2 = CityLab.getInstance().getmCities().get(mPath[i]);distance += Math.sqrt(Math.pow(100 * (c1.getLatitude() - c2.getLatitude()), 2) + Math.pow(100 * (c1.getLongitude() - c2.getLongitude()), 2));}distance += Math.sqrt(Math.pow(100 * (CityLab.getInstance().getmCities().get(mPath[0]).getLatitude() - CityLab.getInstance().getmCities().get(mPath[n - 1]).getLatitude()), 2) + Math.pow(100 * (CityLab.getInstance().getmCities().get(mPath[0]).getLongitude() - CityLab.getInstance().getmCities().get(mPath[n - 1]).getLongitude()), 2));return distance;}

接下来,定义交换对实体类,SO.java,包含两个属性x和y,用来做交换的。

public class SO {private int x;private int y;public SO(int x, int y) {this.x = x;this.y = y;}

最后使用单例模式来管理所有的城市列表CityLab.java

/*** 城市单例*/
public class CityLab {private static CityLab mCityLab = null;private ArrayList<City> mCities = new ArrayList<>();private CityLab() {//读取文件,添加城市信息到mcities中String filename = "d://city.csv";String strbuff;BufferedReader data = null;try {data = new BufferedReader(new InputStreamReader(new FileInputStream(filename)));int id = 1;while (true) {//读取一行数据:北京,116.41667,39.91667strbuff = data.readLine();if (strbuff == null) {break;}String[] arr = strbuff.split(",");City c = new City(id, arr[1], valueOf(arr[2]), valueOf(arr[3]));id++;mCities.add(c);}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}public static CityLab getInstance() {if (mCityLab == null) {mCityLab = new CityLab();}return mCityLab;}public ArrayList<City> getmCities() {return mCities;}
}

实体类我们定义好了,接下来看具体实现粒子群求解TSP问题的过程。
具体PSO.java类如下:

/*** 粒子群求解TSP旅行商问题* 更新公式:Vii=wVi+ra(Pid-Xid)+rb(Pgd-Xid)*/
public class PSO {private int scale; //种群规模private ArrayList<Unit> mUnits = new ArrayList<>();   //粒子群private int MAX_GEN;// 迭代次数private float w;    //惯性权重private int cityNum = 25;    //城市数量private HashMap<Integer, Unit> Pd = new HashMap<>();    //一颗粒子历代中出现最好的解private Unit Pgd;   // 整个粒子群经历过的的最好的解,每个粒子都能记住自己搜索到的最好解private int bestT;// 最佳出现代数private ArrayList<ArrayList<SO>> listV = new ArrayList<>();    //自身交换序列,即所谓的惯性因子Random random = new Random();/*** 构造方法** @param scale* @param MAX_GEN* @param w*/public PSO(int scale, int MAX_GEN, float w) {this.scale = scale;this.MAX_GEN = MAX_GEN;this.w = w;}/*** 初始化参数配置*/private void init() {cityNum = CityLab.getInstance().getmCities().size();random = new Random(System.currentTimeMillis());}/*** 初始化种群*/private void initGroup() {for (int k = 0; k < scale; k++) {int[] path = new int[cityNum];for (int i = 0; i < cityNum; ) {//随机生成一个城市路径//int s = random.nextInt(max)%(max-min+1) + min;int s = random.nextInt(65535) % cityNum;int j;for (j = 0; j < i; j++) {if (s == path[j]) {break;}}if (i == j) {path[i] = s;i++;}}Unit unit = new Unit(path);mUnits.add(unit);}}/*** 初始化自身的交换序列即惯性因子*/private void initListV() {for (int i = 0; i < scale; i++) {ArrayList<SO> list = new ArrayList<>();int n = random.nextInt(cityNum - 1) % (cityNum);    //随机生成一个数,表示当前粒子需要交换的对数for (int j = 0; j < n; j++) {//生成两个不相等的城市编号x,yint x = random.nextInt(cityNum - 1) % (cityNum);int y = random.nextInt(cityNum - 1) % (cityNum);while (x == y) {y = random.nextInt(cityNum - 1) % (cityNum);}//x不等于ySO so = new SO(x, y);list.add(so);}listV.add(list);}}public void solve() {initGroup();initListV();//挑选最好的个体for (int i = 0; i < scale; i++) {Pd.put(i, mUnits.get(i));}Pgd = Pd.get(0);for (int i = 0; i < scale; i++) {if (Pgd.getFitness() > Pd.get(i).getFitness()) {Pgd = Pd.get(i);}}System.out.println("初始化最好结果为:" + Pgd.getFitness());Pgd.printPath();// 进化evolution();// 打印System.out.println("==================最后粒子群=====================");System.out.println("最佳长度出现代数:");System.out.println(bestT);System.out.println("最佳长度");System.out.println(Pgd.getFitness());System.out.println("最佳路径:");Pgd.printPath();}/*** 进化*/private void evolution() {for (int t = 0; t < MAX_GEN; t++) {for (int k = 0; k < scale; k++) {ArrayList<SO> vii = new ArrayList<>();//更新公式:Vii=wVi+ra(Pid-Xid)+rb(Pgd-Xid)//第一部分,自身交换对int len = (int) (w * listV.get(k).size());for (int i = 0; i < len; i++) {vii.add(listV.get(k).get(i));}//第二部分,和当前粒子中出现最好的结果比较,得出交换序列//ra(Pid-Xid)ArrayList<SO> a = minus(mUnits.get(k).getPath(), Pd.get(k).getPath());float ra = random.nextFloat();len = (int) (ra * a.size());for (int i = 0; i < len; i++) {vii.add(a.get(i));}//第三部分,和全局最优的结果比较,得出交换序列//rb(Pgd-Xid)ArrayList<SO> b = minus(mUnits.get(k).getPath(), Pgd.getPath());float rb = random.nextFloat();len = (int) (rb * b.size());for (int i = 0; i < len; i++) {vii.add(b.get(i));}listV.remove(0);    //移除当前,加入新的listV.add(vii);//执行交换,生成下一个粒子exchange(mUnits.get(k).getPath(), vii);}//更新适应度的值,并挑选最好的个体for (int i = 0; i < scale; i++) {mUnits.get(i).upDateFitness();if (Pd.get(i).getFitness() > mUnits.get(i).getFitness()) {Pd.put(i, mUnits.get(i));}if (Pgd.getFitness() > Pd.get(i).getFitness()) {Pgd = Pd.get(i);bestT = t;}}//打印当前代的结果if (t % 100 == 0) {// 打印System.out.println("--------第"+t+"代的最佳结果为-----------");System.out.println(Pgd.getFitness());System.out.println("最佳路径:");Pgd.printPath();}}}/*** 执行交换,更新粒子** @param path* @param vii  存储的是需要交换的下标对*/private void exchange(int[] path, ArrayList<SO> vii) {int tmp;for (SO so : vii) {tmp = path[so.getX()];path[so.getX()] = path[so.getY()];path[so.getY()] = tmp;}}/*** 生成交换对,把a变成和b一样,返回需要交换的下标对列表** @param a* @param b* @return*/private ArrayList<SO> minus(int[] a, int[] b) {int[] tmp = a.clone();ArrayList<SO> list = new ArrayList<>();int index = 0;for (int i = 0; i < b.length; i++) {if (tmp[i] != b[i]) {//在tmp中找到和b[i]相等的值,将下标存储起来for (int j = i + 1; j < tmp.length; j++) {if (tmp[j] == b[i]) {index = j;break;}}SO so = new SO(i, index);list.add(so);}}return list;}public static void main(String[] args) {PSO pso = new PSO(30, 5000, 0.5f);pso.init();pso.solve();}
}

注释写得很详细,这里不再赘述,各位可以直接运行代码,通过debug的方式,这样我觉得很容易理解的。看源码是学习编程最快的方式

最后感谢大家的观看,如果有问题请评论区留言,本人将尽力解答。

4. 运行结果展示

5. 源码下载链接

附上源码下载链接:
https://download.csdn.net/download/u012324136/10513646

声明
在参考下面链接的基础上,对代码进行重构,使用java面向对象的思想,是代码可读性大大增强

参考链接:
https://blog.csdn.net/zuochao_2013/article/details/53431767?ref=myread
https://blog.csdn.net/wangqiuyun/article/details/12515203
https://blog.csdn.net/guognib/article/details/30034821

粒子群算法求解旅行商问题TSP (JAVA实现)相关推荐

  1. 粒子群算法求解旅行商问题

    算法原理 旅行商问题是一个经典的NP问题,假设有N个城市,需要确定一个访问顺序,使得每个城市都访问一面,最后回到起点城市,且保证行走的总距离最短.        假设随机生成10个城市坐标,城市之间的 ...

  2. 【PSO TSP】基于matlab GUI粒子群算法求解旅行商问题【含Matlab源码 1334期】

    ⛄一.TSP简介 旅行商问题,即TSP问题(Traveling Salesman Problem)又译为旅行推销员问题.货郎担问题,是数学领域中著名问题之一.假设有一个旅行商人要拜访n个城市,他必须选 ...

  3. 粒子群算法java_基于粒子群算法求解求解TSP问题(JAVA)

    一.TSP问题 TSP问题(Travelling Salesman Problem)即旅行商问题,又译为旅行推销员问题.货郎担问题,是数学领域中著名问题之一.假设有一个旅行商人要拜访n个城市,他必须选 ...

  4. 【路径规划-TSP问题】基于粒子群结合蚁群算法求解旅行商问题附matlab代码

    1 内容介绍 一种基于粒子群优化的蚁群算法求解TSP问题的方法.该方法在求解TSP问题时,利用粒子群优化的思想,对蚁群算法的参数取值进行优化并选择.在粒子群算法中,将蚁群算法的5个参数(q,α,β,ρ ...

  5. 【ACO TSP】基于matlab改进的蚁群算法求解旅行商问题【含Matlab源码 242期】

    ⛄一.TSP简介 旅行商问题,即TSP问题(Traveling Salesman Problem)又译为旅行推销员问题.货郎担问题,是数学领域中著名问题之一.假设有一个旅行商人要拜访n个城市,他必须选 ...

  6. 【ACO TSP】基于matlab GUI蚁群算法求解旅行商问题【含Matlab源码 1032期】

    ⛄一.TSP简介 旅行商问题,即TSP问题(Traveling Salesman Problem)又译为旅行推销员问题.货郎担问题,是数学领域中著名问题之一.假设有一个旅行商人要拜访n个城市,他必须选 ...

  7. 粒子群算法求解多元函数最值问题

    粒子群算法求解多元函数最值问题 一.简介 多元函数极值&最值问题通常使用导数/偏导数进行推导,这里尝试使用启发式算法进行求解近似最优解.选用粒子群算法进行求解,粒子群算法模仿鸟群觅食行为,核心 ...

  8. 粒子群算法求解带约束优化问题 源码实现

    算法原理 之前求解的无约束的问题. 粒子群算法求解无约束优化问题 源码实现 算法原理如下 今天讲解下求解约束优化的问题.该问题常用的方法是罚函数法.即如果一个解x不满足约束条件,就对适应度值设置一个惩 ...

  9. MATLAB粒子群算法求解带充电站(桩)的电动车辆路径规划EVRP问题代码实例

    MATLAB粒子群算法求解带充电站(桩)的电动车辆路径规划EVRP问题代码实例 问题实例描述: 现有一个配送中心需要向20个客户点进行送货.每个客户点有不同货物需求量和卸货服务时间.配送中心和客户点的 ...

最新文章

  1. (HDU)1091 --A+B for Input-Output Practice (III)(输入输出练习(III))
  2. ES6新特征总结与介绍——声明与表达式
  3. IOS 中视图控制器的生命周期
  4. KMP算法的一个C++实现
  5. 《编译原理》一道关于逆波兰式的作业题(学委推导出了逆波兰式的数学公式表示)
  6. Qt: QMutex
  7. python遗传算法工具箱的使用_Python遗传算法框架——Geatpy学习笔记(一)
  8. Android Wifi控制方法总结
  9. TIFF图像文件格式详解——转载
  10. html.ex.day02
  11. 英伟达Jetson Nano的初步了解
  12. overleaf 只能用jpg格式?visio生成jpg文件太大怎么办?
  13. CocosCreator2.3.3 Effect中换算图集中UV
  14. Salesforce学习之路-developer篇(五)一文读懂Aura原理及实战案例分析
  15. 智慧政务行业发展报告
  16. 一键查看pip已装模块的安装路径
  17. Python的列表推导式和嵌套列表推导式
  18. ios13测试版怎么卸载软件,苹果手机升ios13.2后怎么删除app ios13.2卸载软件应用方法...
  19. 电气自动化需要学c语言,电气自动化专业需要学C语言吗?
  20. 计算机英语二国家线,预测考研英语二国家线及如何通过

热门文章

  1. 怎么让热图显示基因名_热图怎么做? | 热图函数pheatmap()
  2. 数据加密技术基本概念整理
  3. JAVA使用HttpClient模拟登录正方教务系统,爬取学籍信息和课程表成绩等,超详细登录分析和代码注解
  4. FRAM、NRAM、ReRAM
  5. 【程序员必备】免费常用API收藏
  6. JVM基础 之温绍锦讲Java虚拟机基础
  7. Python表白代码合集:5种表白代码、比小心心、无限弹窗、520表白墙、抖音热门表白小软件、无套路表白
  8. 学习RHCSA的第二天
  9. 网易炉石传说盒子 v1.0.3.1 官方版
  10. CAD 开发--卸载外部参照(UnlodeExteralReference)