问题描述
设有n个城市和距离矩阵D=[dij],其中dij表示城市i到城市j的距离,i,j=1,2 … n,则问题是要找出遍访每个城市恰好一次的一条回路并使其路径长度为最短。
算法设计
给定一个初始解,可以是随机生成的也可以是一个顺序序列。给定一个足够大的初始温度和每个温度下迭代的次数。每次迭代产生一个新的解,根据Metropolis准则来判断是否接受新的解。模拟退火算法总是接受更好的解并以一定的概率接受较差的解,温度高时接受较差解的概率更高。
可以用两种方式来生成新的解:
随机选择两个城市,交换它们在当前解中的位置;
随机选择两个城市,将这两个城市之间的路径逆置。
注意要选择好迭代的次数及退火系数。
程序流程
- 给定初始温度T0,每个温度下的迭代次数L,退火系数delta,以及初始的路径P0;
- 对当前温度T重复L次(3)-(4)
- 对当前路径P1随机产生一个扰动得到一条新路径P2,计算路径P1和P2之间的代价差df = f(P2) – f(P1)
- 若df < 0,则接受P2作为新的路径,P1 = P2;否则,若exp(-df/T)>rand,则接受P2,P1 =
P2,否则保留当前解。 - 降低温度,若当前温度达到设定的温度,则停止;否则回到(2)
代码实现
代码中主要分为三个类:City、Path、SA。
City类中主要实现生成城市数量以及城市之间距离,都是随机生成。
public class City {
private int city_num;
private double[][] city_dist;
public City() {
this.city_num = (int) (5 + Math.random() * 100 % 100);
System.out.println("城市数量:" + this.city_num);
this.city_dist = new double[this.city_num][this.city_num];
for (int i = 0; i < this.city_num; i++) {
for (int j = 0; j < this.city_num; j++) {
this.city_dist[i][j] = 0;
}
}
// 产生(1,100)的随机数
for (int i = 0; i < this.city_num; i++) {
for (int j = i + 1; j < this.city_num; j++) {
this.city_dist[i][j] = (int) (1 + Math.random() * 100 % 100);
this.city_dist[j][i] = this.city_dist[i][j];
}
}
for (int i = 0; i < this.city_num; i++) {
for (int j = 0; j < this.city_num; j++) {
System.out.print(city_dist[i][j] + " ");
}
System.out.println();
}
}
Path类中主要是用初始化最佳路径及其长度,还有就是产生一个扰动,产生一条新的路径,即新的解。代码中是随机找到两个城市,将这两个城市之间的距离逆置形成新解。
public class Path {
private City city;
private double res;
private int[] best_path;
private double temp_res; // 临时存放路径及其及其长度
private int[] temp_path;
public Path() {
city = new City();
res = 0.0;
best_path = new int[city.getCity_num()];
temp_res = 0.0;
temp_path = new int[city.getCity_num()];
// 初始化最佳路径以及路径长度
for (int i = 0; i < city.getCity_num(); i++) {
best_path[i] = i;
if (i != city.getCity_num() - 1)
res += city.getDistance(i, i + 1);
}
res += city.getDistance(city.getCity_num() - 1, 0);
}
public void generateNeighour() {
setTemp_path(best_path);
int i = 0;
int j = 0;
while (i == j) {
i = (int) (city.getCity_num() * Math.random());
j = (int) (city.getCity_num() * Math.random());
if (i > j) {
int x = i;
i = j;
j = x;
}
}
while (i < j) {
int exchange = temp_path[i];
temp_path[i] = temp_path[j];
temp_path[j] = exchange;
i++;
j--;
}
double dist = 0.0;
for (int index = 0; index < city.getCity_num() - 1; index++) {
dist += city.getDistance(temp_path[index], temp_path[index + 1]);
}
dist += city.getDistance(temp_path[city.getCity_num() - 1], temp_path[0]);
setTemp_res(dist);
}
public City getCity() {
return city;
}
public int[] getBest_path() {
return best_path;
}
public double getRes() {
return res;
}
public void setRes(double res) {
this.res = res;
}
public void setBest_path(int[] best_path) {
int length = best_path.length;
for(int i = 0;i < length; i++)
this.best_path[i] = best_path[i];
}
public double getTemp_res() {
return temp_res;
}
public void setTemp_res(double temp_res) {
this.temp_res = temp_res;
}
public int[] getTemp_path() {
return temp_path;
}
public void setTemp_path(int[] temp_path) {
int length = temp_path.length;
for(int i = 0; i< length ;i++)
this.temp_path[i] = temp_path[i];
}
}
SA中就是模拟退火算法的主要过程,主要就是SimulatedAnnealing()函数。如果看得懂模拟退火算法的伪码,就可以照着写出。
public class SA {
private double T0; // 起始温度
private double Tn; // 终止温度
private int iterator_num; // 迭代次数
private double alpha; // 温度下降速率
private Path path;
private int s;
public SA(double t0, double tn, int iterator_num, double alpha) {
this.T0 = t0;
this.Tn = tn;
this.iterator_num = iterator_num;
this.alpha = alpha;
this.path = new Path();
this.s = 0;
}
// 模拟退火算法
public void SimulatedAnnealing() {
double t0 = this.T0;
int bChange;
while (t0 > this.Tn) {
bChange = 0;
for (int i = 0; i < iterator_num; i++) {
path.generateNeighour();
double delta = path.getTemp_res() - path.getRes();
if (delta <= 0) {
path.setRes(path.getTemp_res());
path.setBest_path(path.getTemp_path());
bChange = 1;
} else {
double random = Math.random();
double eps = Math.exp(-delta / t0);
if (eps > random && eps < 1) {
path.setRes(path.getTemp_res());
path.setBest_path(path.getTemp_path());
bChange = 1;
}
}
}
t0 *= alpha;
if (bChange == 0)
s++;
else
s = 0;
if (s == 2)
break;
}
}
public static void main(String[] args) {
long begin_time = System.currentTimeMillis();
SA sa = new SA(100, 0.000000005, 100, 0.95);
sa.SimulatedAnnealing();
long end_time = System.currentTimeMillis();
System.out.print("最佳路径长度为:");
System.out.println(sa.path.getRes());
System.out.print("路径顺序:");
for (int i = 0; i < sa.path.getCity().getCity_num(); i++)
System.out.print(sa.path.getBest_path()[i] + " ");
System.out.println();
System.out.println("运行时间:" + ((end_time - begin_time) / 1000.0));
// System.out.println();
// double dist = 0.0;
// for (int i = 0; i < sa.path.getCity().getCity_num() - 1; i++)
// dist += sa.path.getCity().getDistance(sa.path.getBest_path()[i],sa.path.getBest_path()[i+1]);
// dist += sa.path.getCity().getDistance(sa.path.getBest_path()[sa.path.getCity().getCity_num() - 1],sa.path.getBest_path()[0]);
// System.out.println(dist);
}
}
最后就是运行调参了,这个就自己手动去调试吧,过程是无限逼近最优解,最后结果大概率可以得到最优解。如果不是最优解也没事,思想领悟到就行。
过两天再把模拟进化算法求解TSP算法写一下。