遗传算法 | Java版GA_TSP(我的第一个Java程序)
嗯哼,第一次写博客,准确说是第一次通过文字的方式记录自己的工作,闲话少叙,技术汪的博客就该直奔技术主题(关于排版问题,会在不断写博客的过程中慢慢学习,先将就着用吧,重在技术嘛~~~)。
遗传算法(Genetic Algorithm, GA),作为很多人接触智能优化算法的第一个算法,互联网上关于遗传算法的资料不可谓不多,但由于其不是本文的重点,故在此不过细展开,只简单说下大概思想:根据现代生物学理论 “物竞天择,适者生存” 原理,不断淘汰适应能力差的个体,模拟生物进化过程。大致步骤为:
- 生成一个初始种群(Initial Population);
- 通过计算种群中各个个体(Individual)的适应度(Fitness)大小来表示种群中各个个体的对环境的适应能力;
- 根据 “适者生存” 原则选择(Select)部分个体繁衍(即Cross、Mutation操作)生成子代种群;
- 判断是否满足进化结束条件(即算法终止条件). 若满足,结束进化,输出结果;否则,重复执行步骤2~3.
[注]本文用于求解TSP的遗传算法并非经典遗传算法,而是针对TSP特征写的一个简化版的遗传算法,求解质量比较low,毕竟是第一个java程序,还是要预留些改进空间么不是~~~,下一篇文章将针对本文所用的遗传算法进行改进,给个传送门(Java版GA_TSP (2))。
旅行商问题(Travel Salesman Problem, TSP)作为运筹学分支组合优化中的经典问题,一直备受关注。大白话的说法是:一个旅行商从某一城市出发,希望以最短的路程完成对N个城市的巡回访问,并最终回到出发城市。专业一点的说法是:在有向图中,从某一点开始,以最小代价不重不漏的遍历所有点,并返回初始点。
关于Java必须多说两句。首先,Java语言是新学的,之前一直用的MATLAB,然而实验室的工作站带MATLAB还可以,自己的破笔记本实在带不动。感觉每次用MATLAB运行代码,我的小本都会经历一场生死,从点击“Run” 360小球噌的一下窜上95%那一瞬间,仿佛全世界就是剩下了小本呼哧呼哧的挣扎。
本着人道主义精神,终于在某个深夜卸载了MATLAB,顿时感觉小本的重量都轻了许多(可能是幻觉吧)。MATLAB是卸载了,但毕业还要写代码啊,总不能不毕业吧,所以之后选用Python试了试,相比动辄十几个G的MATLAB,几十M的Python用起来确实不错,在加上Python语言本身确实简单,对代码汪想法的快速程序表达非常友好,以及那句“人生苦短,我用Python”,使得我一度认为Python应该就是我的最终选择了,但是……但是……但是用Python跑了GA&TSP后我就动摇了,运行速度着实堪忧,可能是自己技术太渣,即使使用Pypy也不能达到我的预期,而作为对算法实现效率有着苛刻要求的组合优化问题,为了把其他论文中的求解结果给PK下去,所以……所以……所以我又抛弃了Python。
之所以没有选用高效率的且大一就学过的C语言,则是因为实在搞不定C语言的内存管理,对于指针的使用更是随心而动,致使最后Debug的时候就像二哈咬刺猬,都不知道从哪下口……
嗯哼,然后就是Java了,选择Java就是在使用了排除法后的选择,兼顾了我笔记本的性能缺陷与运行效率。说了这么多,终于到达战场,干货出场:
遗传算法示意图
下面上代码:
Data类:用来放客户点的坐标,也可以用I/O流从外部文件中获取。
package GA; public class Data {
public static double[][] XY(){
double [][] xy = new double [][] {
{ 1304 , 2312 } ,
{ 3639 , 1315 } ,
{ 4177 , 2244 } ,
{ 3712 , 1399 } ,
{ 3488 , 1535 } ,
{ 3326 , 1556 } ,
{ 3238 , 1229 } ,
{ 4196 , 1004 } ,
{ 4312 , 790 } ,
{ 4386 , 570 } ,
{ 3007 , 1970 } ,
{ 2562 , 1756 } ,
{ 2788 , 1491 } ,
{ 2381 , 1676 } ,
{ 1332 , 695 } ,
{ 3715 , 1678 } ,
{ 3918 , 2179 } ,
{ 4061 , 2370 } ,
{ 3780 , 2212 } ,
{ 3676 , 2578 } ,
{ 4029 , 2838 } ,
{ 4263 , 2931 } ,
{ 3429 , 1908 } ,
{ 3507 , 2367 } ,
{ 3394 , 2643 } ,
{ 3439 , 3201 } ,
{ 2935 , 3240 } ,
{ 3140 , 3550 } ,
{ 2545 , 2357 } ,
{ 2778 , 2826 } ,
{ 2370 , 2975 }
};
return xy;
}
}
DistanceMatrix类:用来计算各客户点间的欧氏距离。原打算也放在GATSP类中,但考虑到有些问题中距离矩阵是给定的,而不是通过公式计算得到的,就单独拿出来了。
package GA; public class DistanceMatrix {
public static double[][] DistMatrix(double[][] xy){
//计算距离矩阵
int N = xy.length;
double[][] DM = new double[N][N];
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
DM[i][j] = Math.hypot(xy[i][0] - xy[j][0],xy[i][1] - xy[j][1]);
}
}
return DM;
}
}
Pop类:用来放置对Pop的操作方法,包括计算适应度的fit()函数(貌似这个命名不规范,但是我懒呀,我就不改,你能怎样???)和寻找并记录最有个体的Max()函数(写这个函数以及初始化种群的时候非常怀恋MATLAB,MATLAB中自带的的max()能够同时获取最大值的索引,初始化种群就更简单了,一个randperm()解决所有问题。)
package GA; public class Pop { public static double fit(int[] Ind) {
//计算适应度函数
double[][] xy = Data.XY();
double[][] DM = DistanceMatrix.DistMatrix(xy);
double dist = 0.0;
int s = Ind.length; for (int i0 = 0; i0 < s - 1; i0++) {
dist += DM[Ind[i0] - 1][Ind[i0 + 1] - 1];
}
dist += DM[Ind[Ind.length - 1] - 1][Ind[0] - 1];
return 1/dist;
} public static double[] Max(double[] Fit) {
//寻找最优个体及相应的位置
double[] max = new double[2];
double maxFit = 0.0;
double maxIndex = -1;
for (int i = 0; i < Fit.length; i++) {
if (Fit[i] > maxFit) {
maxFit = Fit[i];
maxIndex = (double)i;
}
}
max[0] = maxFit;
max[1] = maxIndex;
return max;
}
}
Sharking类:存放扰动算子,想着以后其他智能算法中也能用得着,就一次性造好轮子,以后就能直接用了,写这几个扰动算子的时候也想夸夸MATLAB强大的轮子库, 如fliplr() 就是很好的轮子,还有对数组的各种切割、拼接神奇操作,让我只能一边痛苦的造着轮子,一边默念“效率第一,Java无敌”……
package GA; import java.util.Random; public class Sharking { public static int[] Swap(int[] S) {
//交换操作 Random rand = new Random();
int I = rand.nextInt(S.length);
int J = rand.nextInt(S.length); int tmp = S[I];
S[I] = S[J];
S[J] = tmp; return S;
} public static int[] Flip(int[] S) {
//翻转操作 int[] S0 = new int [S.length]; Random rand = new Random();
int tmpI = rand.nextInt(S.length);
int tmpJ = tmpI;
while(tmpI==tmpJ) {
tmpJ = rand.nextInt(S.length);
}
int I = Math.min(tmpI, tmpJ);
int J = Math.max(tmpI, tmpJ); for (int i = 0; i < S0.length;i++) {
if (i >= I && i <= J) {
S0[i] = S[I+J-i];
}else {
S0[i] = S[i];
}
}
return S0;
} public static int[] Insert(int[] S) {
//插入操作 int[] S0 = new int [S.length]; Random rand = new Random();
int tmpI = rand.nextInt(S.length);
int tmpJ = tmpI;
while(tmpI==tmpJ) {
tmpJ = rand.nextInt(S.length);
}
int I = Math.min(tmpI, tmpJ);
int J = Math.max(tmpI, tmpJ); for (int i = 0; i < S0.length;i++) {
if (i >= I && i < J) {
S0[i] = S[i+1];
}else if(i==J){
S0[i] = S[I];
}else{
S0[i] = S[i];
}
}
return S0;
}
}
GATSP类:本来准备将算法单独弄一个类,后来……后来……我懒呀、我懒呀…, 所以现在的这个类又臭又长,既包括算法,也包括其他轮子没有干的所有事情,比如说结果输出。
package GA; import java.util.Random; public class GATSP { public static double[] Cusume(double[] A) {
//适应于轮盘赌的累加器
double[] cus = new double[A.length+1]; cus[0] = 0.0;
for (int i = 0; i < A.length; i++) {
cus[i+1] = cus[i] + A[i];
}
return cus;
} public static double Sum(double[] Arr) {
//求和
double sum = 0.0;
for (int i = 0;i <Arr.length;i++) {
sum += Arr[i];
}
return sum;
} public static void main(String[] args) { long startTime=System.currentTimeMillis(); //参数列表
//31城市TSP最优解15377.711
int MaxGen = 500;
int PopSize =200;
double[][] xy = Data.XY();
int N = xy.length; double[][] DM = DistanceMatrix.DistMatrix(xy);
int[][] Pop = new int[PopSize][N];
double[] Trace = new double[MaxGen];
Pop nowPop = new Pop();
double bs = 1e10;
int[] BS = new int[N]; //生成初始种群
for (int p = 0; p < PopSize; p++) {
for (int j = 0; j < N;j++) {
Pop[p][j] = j + 1;
}
//随机生成初始个体
for (int k = 0; k < N;k++) {
Random rand = new Random();
int r = rand.nextInt(N);
int tmp;
tmp = Pop[p][r];
Pop[p][r] = Pop[p][k];
Pop[p][k] = tmp;
}
} //进入迭代
for (int gen = 0; gen < MaxGen;gen++) {
// 计算种群适应度
double[] Fit = new double[PopSize];
int[] indiv = new int[N]; for (int p = 0; p < PopSize;p++) {
//取一个个体
for (int j = 0; j < N;j++) {
indiv[j] = Pop[p][j];
}
Fit[p] = nowPop.fit(indiv);
} //更新最优个体以及最优个体的适应度
double[] SortAfterFit = new double[PopSize];//拷贝一份适应度数组
for (int i = 0; i < PopSize;i++) {
SortAfterFit[i] = Fit[i];
}
double[] BestS = nowPop.Max(Fit);
double tmpbs = 1/BestS[0]; //当前代最优解(最优个体的适应度)
if (tmpbs < bs) {
bs = tmpbs;
int BestIndex = (int)BestS[1];
for (int i = 0; i < N; i++) {
BS[i] =Pop[BestIndex][i]; //最优个体
}
}
Trace[gen] = bs; //轮盘赌选择
double[] cusFit0 = Cusume(Fit);//数组长度变为N+1,补充了首个元素0.0
double sumFit = Sum(Fit);
//归一化
double[] cusFit = new double[cusFit0.length];
for (int i = 0; i < cusFit.length; i++) {
cusFit[i] =cusFit0[i] / sumFit;
} int[][] newPop = new int[PopSize][N];
for (int q = 0;q < N; q++) {
newPop[0][q] = BS[q];
}
for (int p = 1; p < PopSize; p++) {
double rand = Math.random();
for (int r = 0; r < PopSize; r++) {
if (rand > cusFit[r] && rand <= cusFit[r+1]) {
for (int q = 0;q < N; q++) {
newPop[p][q] = Pop[r][q];
}
}
}
} //扰动操作
for (int p = 0; p < PopSize; p++) {
double R = Math.random(); int[] S = new int[N];
for (int i = 0; i < N; i++) {
S[i] = newPop[p][i];
} int[] S0 = new int[N];
if (R < 0.33) {
S0 = Sharking.Swap(S);
}else if (R > 0.67) {
S0 = Sharking.Insert(S);
}else {
S0 = Sharking.Flip(S);
} for (int i = 0; i < N; i++) {
newPop[p][i] = S0[i];
}
} //更新种群
for (int p = 0; p < PopSize; p++) {
for (int i = 0; i < N; i++) {
Pop[p][i] = newPop[p][i];
}
}
}//结果迭代 long endTime=System.currentTimeMillis();
//结果输出
System.out.println("经过"+MaxGen+"次迭代,最短路径长度为:"+bs);
System.out.println("程序用时 "+(double)(endTime - startTime)/1000+"秒.");
double bs0 = 15377.711;
System.out.println("与最优解的误差为"+(bs-bs0)/bs0*100+"%.");
for (int i = 0; i < MaxGen; i++) {
System.out.println(i+1+" "+Trace[i]);
} System.out.println("相应的最短路径为");
for (int i = 0; i < N; i++) {
System.out.print(BS[i]+"->");
}
System.out.print(BS[0]);
}
}
本来剧情发展到这里,应该是求解结果展示,然后就可以领盒饭了,无奈结果太渣,不忍心拿出来,所以剧情稍微转下,多说几句关于MATLAB与Java:
本来应该还有一个PlotFigure类,然而Java学的不精,暂时还不会用,说到这里,又怀恋起MATLAB来,其强大的可视化结果能力,确实很有吸引力,想想之前只要用MATLAB写上几行代码就可以在每次跑完程序有一堆酷酷的图出来,比如说算法迭代收敛图、最优路径图什么的,现在只有“28->23->29->19->……”这种结果,心里实在不是很平衡,所以,我决定……下次学习下Java的绘图。
Ps:还是截图纪念下第一次Java写遗传算法~~~
[完]
遗传算法 | Java版GA_TSP(我的第一个Java程序)的更多相关文章
- 遗传算法 | Java版GA_TSP (2)
嗯哼,上一篇博客中用Java实现了遗传算法求解TSP(Java版GA_TSP(我的第一个Java程序)),但明显求解效果不太好,都没太好意思贴出具体的结果,今天捣腾了下,对算法做了一些小改进,求解效果 ...
- 遗传算法 | C++版GA_TSP
嗯哼,时隔半年,再次有时间整理关于组合优化问题——旅行商问题(Traveling Salesman Problem, TSP),这次采用的是经典遗传算法(Genetic Algorithm, GA)进 ...
- 用java自带jdk开发第一个java程序
[学习笔记] 1.用java自带jdk开发第一个java程序: 下面要讲的eclipse要想正常工作,需要先学会配置这里的jdk.jdk要想正常工作,需先学会配置JAVA_HOME和ClassPa ...
- [Java 教程 03] 我的第一个Java程序
现在,大家应该都已经安装好jdk环境了吧!是不是已经跃跃欲试,按耐不住心中的小激动了?那我们现在就来写我们java学习生涯中的第一个java程序. 文件相关设置 为了方便后面大家的学习呢?有一点大家还 ...
- Java面向对象编程 第二章 第一个Java应用
2.1创建Java源文件 Java应用由一个或多个扩展名为".java"的文件构成,这些文件被称为Java源文件,从编译的角度,则被称为编译单元. 本章包含两个Java源文件:Do ...
- 【安装eclipse, 配置java环境教程】 编写第一个java程序
写java通常用eclipse编写,还有一款编辑器比较流行叫IJ.这里我们只说下eclipse编写java的前期工作. 在安装eclipse之前要下载java的sdk文件,即java SE:否则无法运 ...
- Java学习笔记二十九:一个Java面向对象的小练习
一个Java面向对象的小练习 一:项目需求与解决思路: 学习了这么长时间的面向对象,我们只是对面向对象有了一个简单的认识,我们现在来做一个小练习,这个例子可以使大家更好的掌握面向对象的特性: 1.人类 ...
- 【Java】使用记事本运行第一个Java程序
要编写java程序,java sdk必不可少,mac OS系统自带sdk,如果觉得版本太低,可以去官网下载最新的. 打开终端,新建一个HelloWorld.java文件: vim HelloWorld ...
- 【Java.Regex】使用正则表达式查找一个Java类中的成员函数
代码: import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; imp ...
随机推荐
- Vue.js 插件开发
Vue.js 的插件应当有一个公开方法 install .这个方法的第一个参数是 Vue 构造器 , 第二个参数是一个可选的选项对象: MyPlugin.install = function (Vue ...
- js之正则表达式(RegExp对象)
先看一个很有意思的例子: 用字面量的方式定义了一个正则表达式 /\w/g,再重复匹配字符串 ‘ab’ 的时候,出现了结果不唯一的现象. 很多新手都对这种现象感到困惑,难道是正则表达式不稳定吗? 接下来 ...
- Assembly测试
using UnityEngine; using System.Collections; using System.Collections.Generic; using System.Reflecti ...
- 【起航计划 013】2015 起航计划 Android APIDemo的魔鬼步伐 12 App->Activity->SetWallpaper 设置壁纸 WallpaperManager getDrawingCache使用
SetWallpaper介绍如何在Android获取当前Wallpaper,对Wallpaper做些修改,然后用修改后的图像重新设置Wallpaper.(即设置>显示>壁纸>壁纸的功 ...
- Struts2_用ModelDriven接收参数
通过实现 ModelDriven 接口来接收请求参数,这种方法用的比较少,一般还是用前两种. 请求: <a href="user/user!add?name=xiaoer&ag ...
- 微信小程序实现获得用户手机号
具体操作方法如下: 使用方法 需要将 <button> 组件 open-type 的值设置为 getPhoneNumber,当用户点击并同意之后,可以通过 bindgetphonenumb ...
- HCNA配置浮动静态路由
1.拓扑图 2.配置IP R1 Please press enter to start cmd line! ############ <Huawei> Dec ::-: Huawei %% ...
- centos 7(1611)安装笔记
麻烦 前天我把双系统笔记本里的 deepin 的磁盘分区直接从 Windows 7 磁盘管理里格式化了,结果悲催了,开不了机了,显示: 我以为是 Windows 7 的引导没了,就进 PE 修复了 ...
- 如果有反向代理的情况下,获取最原始的IP的办法
HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_HOST"];
- 2019.03.09 ZJOI2019模拟赛 解题报告
得分: \(20+0+40=60\)(\(T1\)大暴力,\(T2\)分类讨论写挂,\(T3\)分类讨论\(40\)分) \(T1\):天空碎片 一道神仙数学题,貌似需要两次使用中国剩余定理. 反正不 ...