旅行商问题,即TSP问题(Traveling Salesman Problem)是数学领域中著名问题之一。假设有一个旅行商人要拜访N个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要 回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。TSP问题是一个NPC问题。这个问题一般是使用遗传算法去解,但是蚂蚁算法要更高效.对于c++不熟悉的我,用C语言是个噩梦,因此写一个java代码,并且桌面窗口显示结果的版本.
再来说说蚁群算法
各个蚂蚁在没有事先告诉他们食物在什么地方的前提下开始寻找食物。当一只找到食物以后,它会向环境释放一 种挥发性分泌物pheromone (称为信息素,该物质随着时间的推移会逐渐挥发消失,信息素浓度的大小表征路径的远近)来实现的,吸引其他的蚂蚁过来,这样越来越多的蚂蚁会找到食物。有 些蚂蚁并没有像其它蚂蚁一样总重复同样的路,他们会另辟蹊径,如果另开辟的道路比原来的其他道路更短,那么,渐渐地,更多的蚂蚁被吸引到这条较短的路上 来。最后,经过一段时间运行,可能会出现一条最短的路径被大多数蚂蚁重复着。(实在不理解去度娘)
公共函数及变量
package com.seul.tsp;
/*
* 公共函数以及变量
*/
public class PublicFun
{
public static final double ALPHA=1.0;//信息启发式因子,信息素的重要程度
public static final double BETA=2.0;//期望启发式因子, 城市间距离的重要程度
public static final double ROU=0.5;//信息素残留系数
public static int N_ANT_COUNT=50;//蚂蚁数量
public static int N_IT_COUNT=200;//迭代次数
public static int N_CITY_COUNT=15;//城市数量
public static final double DBQ=100.0;//总信息素
public static final double DB_MAX=Math.pow(10,9);//一个标志数,用来初始化一个比较大的最优路径
//两两城市间的信息量
public static double[][] g_Trial;
//两两城市间的距离
public static double[][] g_Distance;
//返回指定范围内的随机整数
public static int rnd(int nLow,int nUpper)
{
return (int) (Math.random()*(nUpper-nLow)+nLow);
}
//返回指定范围内的随机浮点数
public static double rnd(double dbLow,double dbUpper)
{
return Math.random()*(dbUpper-dbLow)+dbLow;
}
}
蚂蚁类
package com.seul.tsp;
/*
* 蚂蚁类
* 使用对象的复制,必须实现Cloneable接口,然后重写Object中clone()方法
*/
public class Ant implements Cloneable
{
public int[] m_nPath=new int[PublicFun.N_CITY_COUNT];//蚂蚁走过的路径
public double m_dbPathLength;//蚂蚁走过的路径长度
public int[] m_nAllowedCity=new int[PublicFun.N_CITY_COUNT];//蚂蚁没有去过的城市
public int m_nCurCityNo;//当前所在城市的编号
public int m_nMovedCityCount;//已经去过的城市数量
/*
* 初始化函数,蚂蚁搜索前调用该方法
*/
public void Init()
{
for (int i = 0; i < PublicFun.N_CITY_COUNT; i++)
{
m_nAllowedCity[i]=1;//设置全部城市没有去过
m_nPath[i]=0;//蚂蚁走过的路径全部设置为0
}
m_dbPathLength=0.0; //蚂蚁走过的路径长度设置为0
m_nCurCityNo=PublicFun.rnd(0,PublicFun.N_CITY_COUNT);//随机选择一个出发城市
m_nPath[0]=m_nCurCityNo;//把出发城市保存的路径数组中
m_nAllowedCity[m_nCurCityNo]=0;//标识出发城市已经去过了
m_nMovedCityCount=1;//已经去过的城市设置为1;
}
/*
* 覆盖Object中的clone()方法
* 实现对象的复制
*/
protected Object clone() throws CloneNotSupportedException
{
return super.clone();
}
/*
* 选择下一个城市
* 返回值为城市编号
*/
public int ChooseNextCity()
{
int nSelectedCity=-1;//返回结果,初始化为-1
//计算当前城市和没去过城市的信息素的总和
double dbTotal=0.0;
double[] prob=new double[PublicFun.N_CITY_COUNT];//用来保存各个城市被选中的概率
for (int i = 0; i < PublicFun.N_CITY_COUNT; i++)
{
if(m_nAllowedCity[i]==1)//城市没去过
{
//该城市和当前城市的信息素
prob[i]=Math.pow(PublicFun.g_Trial[m_nCurCityNo][i], PublicFun.ALPHA)*Math.pow(1.0/PublicFun.g_Distance[m_nCurCityNo][i],PublicFun.BETA);
dbTotal=dbTotal+prob[i];//累加信息素
}
else//如果城市去过了 则被选中的概率为0;
{
prob[i]=0.0;
}
}
//进行轮盘选择
double dbTemp=0.0;
if(dbTotal>0.0)//如果总的信息素大于0
{
dbTemp=PublicFun.rnd(0.0, dbTotal);//取一个随机数
for (int i = 0; i < PublicFun.N_CITY_COUNT; i++)
{
if(m_nAllowedCity[i]==1)//城市没有去过
{
dbTemp=dbTemp-prob[i];//相当于轮盘
if(dbTemp<0.0)//轮盘停止转动,记下城市编号,跳出循环
{
nSelectedCity=i;
break;
}
}
}
}
/*
* 如果城市间的信息素非常小 ( 小到比double能够表示的最小的数字还要小 )
* 那么由于浮点运算的误差原因,上面计算的概率总和可能为0
* 会出现经过上述操作,没有城市被选择出来
* 出现这种情况,就把第一个没去过的城市作为返回结果
*/
if(nSelectedCity==-1)
{
for (int i = 0; i < PublicFun.N_CITY_COUNT; i++)
{
if(m_nAllowedCity[i]==1)//城市没有去过
{
nSelectedCity=i;
break;
}
}
}
return nSelectedCity;
}
/*
* 蚂蚁在城市间移动
*/
public void Move()
{
int nCityNo=ChooseNextCity();//选择下一个城市
m_nPath[m_nMovedCityCount]=nCityNo;//保存蚂蚁走过的路径
m_nAllowedCity[nCityNo]=0;//把这个城市设置为已经去过了
m_nCurCityNo=nCityNo;//改变当前城市为选择的城市
m_nMovedCityCount++;//已经去过的城市加1
}
/*
* 蚂蚁进行一次搜索
*/
public void Search()
{
Init();//蚂蚁搜索前,进行初始化
//如果蚂蚁去过的城市数量小于城市数量,就继续移动
while (m_nMovedCityCount<PublicFun.N_CITY_COUNT)
{
Move();//移动
}
//完成搜索后计算走过的路径长度
CalPathLength();
}
/*
* 计算蚂蚁走过的路径长度
*/
public void CalPathLength()
{
m_dbPathLength=0.0;//先把路径长度置0
int m=0;
int n=0;
for(int i=1;i<PublicFun.N_CITY_COUNT;i++)
{
m=m_nPath[i];
n=m_nPath[i-1];
m_dbPathLength=m_dbPathLength+PublicFun.g_Distance[m][n];
}
//加上从最后城市返回出发城市的距离
n=m_nPath[0];
m_dbPathLength=m_dbPathLength+PublicFun.g_Distance[m][n];
m_dbPathLength=(Math.round(m_dbPathLength*100))/100.0;
}
}
TSP问题类
package com.seul.tsp;
/*
* Tsp问题类
*/
public class Tsp
{
//蚂蚁数组
public Ant[] m_antAry=new Ant[PublicFun.N_ANT_COUNT];
public Ant[] m_betterAnts=new Ant[PublicFun.N_IT_COUNT];//定义一组蚂蚁,用来保存每一次搜索中较优结果,不参与搜索
public Ant m_bestAnt;//定义一个蚂蚁变量,用来保存最终最优结果,不参与搜索
/*
* 初始化数据
*/
public void InitData() throws CloneNotSupportedException
{
//初始化所有蚂蚁
PublicFun.g_Distance=new double[PublicFun.N_CITY_COUNT][PublicFun.N_CITY_COUNT];
PublicFun.g_Trial=new double[PublicFun.N_CITY_COUNT][PublicFun.N_CITY_COUNT];
m_bestAnt=new Ant();
for (int i = 0; i <PublicFun.N_ANT_COUNT; i++)
{
m_antAry[i]=new Ant();
}
for (int i = 0; i < PublicFun.N_IT_COUNT; i++)
{
m_betterAnts[i]=new Ant();
m_betterAnts[i].m_dbPathLength=PublicFun.DB_MAX;//把较优蚂蚁的路径长度设置为一个很大值
}
//先把最优蚂蚁的路径长度设置为一个很大值
m_bestAnt.m_dbPathLength=PublicFun.DB_MAX;
//随机生成两城市之间的距离
for(int i=0;i<PublicFun.N_CITY_COUNT;i++)
{
for(int j=i;j<PublicFun.N_CITY_COUNT;j++)
{
if(i==j)
PublicFun.g_Distance[i][j]=0.0;//同一个城市为0
else
{
PublicFun.g_Distance[i][j]=PublicFun.rnd(20.0,100.0);//i-j的距离与j-i的距离相等
PublicFun.g_Distance[j][i]=PublicFun.g_Distance[i][j];
}
}
}
//初始化信息素
for(int i=0;i<PublicFun.N_CITY_COUNT;i++)
{
for(int j=0;j<PublicFun.N_CITY_COUNT;j++)
{
PublicFun.g_Trial[i][j]=1.0;
}
}
Iterator();//开始迭代
}
/*
* 更新环境信息素
*/
public void UpdateTrial()
{
//临时数组,保存各只蚂蚁在两两城市间新留下的信息素
double[][] dbTempAry=new double[PublicFun.N_CITY_COUNT][PublicFun.N_CITY_COUNT];
//全部设置为0;
for (int i = 0; i <PublicFun.N_CITY_COUNT; i++)
{
for (int j = 0; j < PublicFun.N_CITY_COUNT; j++)
{
dbTempAry[i][j]=0.0;
}
}
//计算新增加的信息素,保存到临时变量
int m=0;
int n=0;
for(int i=0; i<PublicFun.N_ANT_COUNT;i++)
{
for (int j = 1; j < PublicFun.N_CITY_COUNT; j++)
{
m=m_antAry[i].m_nPath[j];
n=m_antAry[i].m_nPath[j-1];
dbTempAry[n][m]=dbTempAry[n][m]+PublicFun.DBQ/m_antAry[i].m_dbPathLength;
dbTempAry[m][n]=dbTempAry[n][m];
}
//最后城市与开始城市之间的信息素
n=m_antAry[i].m_nPath[0];
dbTempAry[n][m]=dbTempAry[n][m]+PublicFun.DBQ/m_antAry[i].m_dbPathLength;
dbTempAry[m][n]=dbTempAry[n][m];
}
//更新环境信息素
for (int i = 0; i < PublicFun.N_CITY_COUNT; i++)
{
for (int j = 0; j < PublicFun.N_CITY_COUNT; j++)
{
//最新的环境信息素 = 留存的信息素 + 新留下的信息素
PublicFun.g_Trial[i][j]=PublicFun.g_Trial[i][j]*PublicFun.ROU+dbTempAry[i][j];
}
}
}
/*
* 迭代
*/
public void Iterator() throws CloneNotSupportedException
{
//迭代次数内进行循环
for (int i = 0; i < PublicFun.N_IT_COUNT; i++)
{
//每只蚂蚁搜索一遍
for(int j=0;j<PublicFun.N_ANT_COUNT;j++)
{
m_antAry[j].Search();
}
//保存较优结果
for(int j=0;j<PublicFun.N_ANT_COUNT;j++)
{
if (m_antAry[j].m_dbPathLength < m_betterAnts[i].m_dbPathLength)
{
m_betterAnts[i] = (Ant) m_antAry[j].clone();
}
}
UpdateTrial();
}
//找出最优蚂蚁
for(int k=0;k<PublicFun.N_IT_COUNT;k++)
{
if(m_betterAnts[k].m_dbPathLength<m_bestAnt.m_dbPathLength)
{
m_bestAnt=m_betterAnts[k];
}
}
// getBetterAnt();//输出每次的较优路径
// getBestAnt();//输出最佳路径
}
/*
* 输出最佳路径到控制台.(暂不使用,但保留)
*/
public void getBestAnt()
{
System.out.println("最佳路径:");
System.out.println( "路径:"+getAntPath(m_bestAnt)+"长度:"+getAntLength(m_bestAnt));
}
/*
* 输出每次的较优路径到控制台.(暂不使用,但保留)
*/
public void getBetterAnt()
{
System.out.println("每一次的较优路径:");
for (int i = 0; i < m_betterAnts.length; i++)
{
System.out.println("("+i+") 路径:"+getAntPath(m_betterAnts[i])+"长度:"+getAntLength(m_betterAnts[i]));
}
}
/*
* 返回蚂蚁经过的路径
*/
public String getAntPath(Ant ant)
{
String s="";
for(int i=0;i<ant.m_nPath.length;i++)
{
s+=ant.m_nPath[i]+"-";
}
s+=ant.m_nPath[0]; //蚂蚁最后要回到开始城市
return s;
}
/*
* 返回蚂蚁经过的长度
*/
public double getAntLength(Ant ant)
{
return ant.m_dbPathLength;
}
}
界面初始化
package com.seul.tsp;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
/*
* 界面实现
*/
@SuppressWarnings("serial")
public class Frame extends JFrame
{
JLabel headLabel;
JLabel antLabel;
JLabel itLabel;
JLabel cityLabel;
JLabel betterAntLabel;
JLabel bestAntLabel;
JTextField antTF;
JTextField itTF;
JTextField cityTF;
JTextArea betterAntTA;
JTextArea bestAntTA;
JButton beginBtn;
JScrollPane betterAntScroll;
JScrollPane bestAntScroll;
JLabel descLabel;
public Frame()
{
Init();
}
/*
* 界面初始化函数
*/
private void Init()
{
String descStr = "<html><font color=green>说明:<br/>本测试程序两两城市间距离为随机生成20.0~100.0之间的随机浮点数<font/><br/><br/><br/><br/><font color=black>©2016 Seul</font></html>";
headLabel=new JLabel("蚁群算法解决TSP问题演示");
antLabel=new JLabel("请输入蚂蚁数量:");
itLabel=new JLabel("请输入迭代次数:");
cityLabel=new JLabel("请输入城市数量:");
betterAntLabel=new JLabel("每一次迭代的较优路径以及长度");
bestAntLabel=new JLabel("最佳路径以及长度");
antTF=new JTextField("50");
itTF=new JTextField("200");
cityTF=new JTextField("15");
betterAntTA = new JTextArea();
bestAntTA=new JTextArea();
beginBtn=new JButton("开始运行");
betterAntScroll=new JScrollPane(betterAntTA);
bestAntScroll=new JScrollPane(bestAntTA);
descLabel=new JLabel(descStr);
descLabel.setBounds(20,220,150,150);
setBounds(300, 150, 600, 400);
setTitle("蚁群算法解决TSP问题演示程序");
setLayout(null);
setResizable(false);
headLabel.setBounds(0, 0, 600, 50);
headLabel.setHorizontalAlignment(SwingConstants.CENTER);
headLabel.setFont(new Font("隶书", Font.BOLD, 20));
antLabel.setBounds(20, 60, 100, 20);
cityLabel.setBounds(20, 100, 100, 20);
itLabel.setBounds(20, 140, 100, 20);
antTF.setBounds(125, 60, 50, 20);
cityTF.setBounds(125, 100, 50, 20);
itTF.setBounds(125, 140, 50, 20);
beginBtn.setBounds(40, 180, 110, 25);
betterAntLabel.setBounds(200, 60, 200, 20);
betterAntScroll.setBounds(200, 80, 380, 200);
bestAntLabel.setBounds(200, 300, 200, 20);
bestAntLabel.setForeground(Color.red);
bestAntScroll.setBounds(200, 320, 380, 40);
bestAntTA.setBackground(Color.CYAN);
bestAntTA.setFont(new Font("微软雅黑", Font.BOLD, 13));
getContentPane().add(headLabel);
getContentPane().add(antLabel);
getContentPane().add(antTF);
getContentPane().add(itLabel);
getContentPane().add(itTF);
getContentPane().add(cityLabel);
getContentPane().add(cityTF);
getContentPane().add(beginBtn);
getContentPane().add(betterAntLabel);
getContentPane().add(betterAntScroll);
getContentPane().add(bestAntLabel);
getContentPane().add(bestAntScroll);
getContentPane().add(descLabel);
setDefaultCloseOperation(EXIT_ON_CLOSE);
beginBtn.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
try
{
Begin();
} catch (CloneNotSupportedException e1)
{
e1.printStackTrace();
}
}
});
setVisible(true);
}
/*
* 开始运算
*/
public void Begin() throws CloneNotSupportedException
{
betterAntTA.setText("");
bestAntTA.setText("");
String antCountStr=antTF.getText().replaceAll("[^\\d]", "");//去除所有非数字字符
String itCountStr=itTF.getText().replaceAll("[^\\d]", "");
String cityCountStr=cityTF.getText().replaceAll("[^\\d]", "");
if(!antCountStr.equals("")&&!itCountStr.equals("")&&!cityCountStr.equals(""))//不为"";
{
PublicFun.N_ANT_COUNT=Integer.parseInt(antCountStr);// 转换为数字
PublicFun.N_IT_COUNT=Integer.parseInt(itCountStr);
PublicFun.N_CITY_COUNT=Integer.parseInt(cityCountStr);
}
/*
* 实例化Tsp
*/
Tsp tsp=new Tsp();
tsp.InitData();//开始执行
/*
* 把结果显示在窗口中
*/
for (int i = 0; i < tsp.m_betterAnts.length; i++)
{
betterAntTA.append("("+(i+1)+") 路径:"+tsp.getAntPath(tsp.m_betterAnts[i])+"长度:"+tsp.getAntLength(tsp.m_betterAnts[i])+"\n");
}
betterAntTA.setCaretPosition(0);
bestAntTA.append("路径:"+tsp.getAntPath(tsp.m_bestAnt)+"长度:"+tsp.getAntLength(tsp.m_bestAnt));
bestAntTA.setCaretPosition(0);
}
}
入口
package com.seul.tsp;
/*
* 入口类
*/
public class Main
{
public static void main(String[] args) throws CloneNotSupportedException
{
new Frame();
}
}